std/openai - Docs ↗
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 any affordable model – such as gpt-5.6-luna. Val
Town Pro users can make 10 expensive requests (e.g. gpt-5.6-sol) per rolling
24-hour window, and are then bumped down to gpt-5.6-luna.
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:
- Usage Quota: We limit each user to 100 requests per day.
- Features: Chat completions and the Responses API are the only endpoints
available. The Responses API supports the
web_searchtool and structured outputs (text.formatwithjson_schema).
If these limits are too low, let us know! You can also get around the limitation by bringing your own API key.