Edit on val.town ↗

OpenAI

Use OpenAI's chat completion API with std/openai. This integration enables access to OpenAI's language models without needing to acquire API keys.

Val Town free users can use two affordable models: gpt-5.6-luna and gpt-5.4-nano. A request with any other model will substitute gpt-5.6-luna automatically. Val Town Pro users can make 10 expensive requests per 24-hour UTC calendar day to any other model (e.g. gpt-5.6-sol) and are then bumped down to gpt-5.6-luna. All users are limited to 100 requests/day.

This SDK is powered by our openapiproxy.

import { OpenAI } from "https://esm.town/v/std/openai/main.ts";

const openai = new OpenAI();

const completion = await openai.chat.completions.create({
  messages: [{ role: "user", content: "Say hello in a creative way" }],
  model: "gpt-5.6-luna",
  max_completion_tokens: 100,
});

console.log(completion.choices[0].message.content);

The free proxy is great for getting started, but you may outgrow the rate limits. The easiest upgrade path is to bring your own OpenAI API key — just set OPENAI_API_KEY and use the npm:openai client directly when it's available, falling back to ValTownOpenAI otherwise. This way your val works with or without a key.

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts";
import OpenAI from "npm:openai";

// Use your own key if available, otherwise the Val Town proxy.
const apiKey = Deno.env.get("OPENAI_API_KEY");
const openai = apiKey ? new OpenAI({ apiKey }) : new ValTownOpenAI({});

const completion = await openai.chat.completions.create({
  model: "gpt-5.6-luna",
  messages: [{ role: "user", content: "Say hello in one word." }],
});

console.log(completion.choices[0].message.content);

See the runnable example: examples/bring-your-own-key.ts

The Responses API (responses.create) is fully supported through the Val Town proxy. It enables the web_search tool, which lets the model browse the web in real time — incredibly effective for enrichment, research, and anything where fresh data beats stale training knowledge.

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts";

const openai = new ValTownOpenAI({});

const response = await openai.responses.create({
  model: "gpt-5.6-luna",
  tools: [{ type: "web_search" }],
  instructions:
    "You research companies. Given a company name, reply in 2-3 short plain-text lines: what they do, their stage, and why a developer might care. No markdown.",
  input: "Val Town",
});

console.log(response.output_text);

See the runnable example: examples/web-search.ts

Instead of asking the model to "return JSON" and manually parsing the response, pass a JSON schema in text.format. The model is constrained to output JSON that matches your schema exactly — no fence stripping, no regex, no try/catch on malformed JSON. This is the idiomatic approach for classification, scoring, extraction, routing, or any task where you need typed data back.

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts";

const openai = new ValTownOpenAI({});

const response = await openai.responses.create({
  model: "gpt-5.6-luna",
  instructions:
    "You classify user feedback. Given a piece of feedback, classify it and write a one-sentence summary.",
  input:
    "The app keeps logging me out every 5 minutes, it's really frustrating.",
  text: {
    format: {
      type: "json_schema",
      name: "classification",
      schema: {
        type: "object",
        properties: {
          category: {
            type: "string",
            enum: ["bug", "feature_request", "praise", "question"],
          },
          priority: {
            type: "string",
            enum: ["low", "medium", "high"],
          },
          summary: { type: "string" },
        },
        required: ["category", "priority", "summary"],
        additionalProperties: false,
      },
    },
  },
});

// output_text is guaranteed valid JSON matching the schema.
const result = JSON.parse(response.output_text);
console.log(result);

See the runnable example: examples/structured-outputs.ts

To send an image to ChatGPT, the easiest way is by converting it to a data URL, which is easiest to do with @stevekrouse/fileToDataURL.

import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL";

const dataURL = await fileToDataURL(file);
const response = await chat(
  [
    {
      role: "system",
      content: `You are an nutritionist. 
  Estimate the calories.
  We only need a VERY ROUGH estimate.
  Respond ONLY in a JSON array with values conforming to: {ingredient: string, calories: number}
  `,
    },
    {
      role: "user",
      content: [
        {
          type: "image_url",
          image_url: {
            url: dataURL,
          },
        },
      ],
    },
  ],
  {
    model: "gpt-5.6-luna",
    max_completion_tokens: 200,
  },
);

The OpenAI Agents SDK works with std/openai too, including hosted tools like web search:

import { ValTownOpenAI } from "https://esm.town/v/std/openai/main.ts";
import {
  Agent,
  run,
  setDefaultOpenAIClient,
  setTracingDisabled,
  webSearchTool,
} from "npm:@openai/agents";

setTracingDisabled(true); // tracing requires your own OpenAI key
setDefaultOpenAIClient(new ValTownOpenAI({}));

const agent = new Agent({
  name: "Researcher",
  tools: [webSearchTool()],
});

const result = await run(agent, "What's new in Val Town?");
console.log(result.finalOutput);

While our wrapper simplifies the integration of OpenAI, there are a few limitations to keep in mind:

If these limits are too low, let us know! You can also get around the limitation by bringing your own API key.

Last updated