OpenAI Skill
OpenAI integration: Chat Completions, Responses API with agent callbacks, streaming SSE, and Real-Time WebSocket.
Invoke with: /openai
Prerequisites
Enable the AI service in ioto.json5:
json5
{
services: { ai: true, url: true, web: true },
ai: {
enable: true,
provider: 'openai',
model: 'gpt-4o',
endpoint: 'https://api.openai.com/v1',
key: 'sk-proj-...',
},
}Or set OPENAI_API_KEY environment variable instead of the key field.
Key APIs
c
/* Chat Completion -- synchronous request/response */
Json *openaiChatCompletion(Json *props);
/* Responses API -- with optional agent callbacks for tool use */
Json *openaiResponses(Json *props, OpenAIAgent agent, void *arg);
/* Streaming -- SSE events delivered to callback */
Url *openaiStream(Json *props, UrlSseProc callback, void *arg);
/* Models */
Json *openaiListModels(void);Chat Completion
c
static void completionAction(Web *web)
{
Json *response;
if ((response = openaiChatCompletion(web->vars)) == NULL) {
webError(web, 500, "Cannot issue request to OpenAI");
} else {
webWriteJson(web, response);
}
webFinalize(web);
jsonFree(response);
}Responses API
c
Json *response = openaiResponses(web->vars, NULL, 0);
cchar *text = jsonGet(response, 0, "output_text", 0);
jsonFree(response);Agentic Workflow with Tool Callbacks
c
static char *agentCallback(cchar *name, Json *request, Json *response, void *arg)
{
if (smatch(name, "getTemp")) {
return sclone("36.5");
} else if (smatch(name, "callEmergency")) {
return sclone("Ambulance dispatched");
}
return sclone("Unknown function");
}
static char *runWorkflow(cchar *input)
{
Json *request = jsonAlloc();
jsonSetString(request, 0, "input", input);
jsonSetString(request, 0, "model", ioGetConfig("ai.model", "gpt-4o-mini"));
jsonSetString(request, 0, "instructions",
"You are a doctor monitoring patient temperature.");
jsonSetJsonFmt(request, 0, "tools", "%s",
SDEF([{
type: 'function',
name: 'getTemp',
description: 'Get the patient temperature',
}, {
type: 'function',
name: 'callEmergency',
description: 'Call emergency response',
}]));
Json *response = openaiResponses(request, agentCallback, 0);
char *text = sclone(jsonGet(response, 0, "output_text", 0));
jsonFree(request);
jsonFree(response);
return text;
}Streaming SSE
c
static void streamCallback(Url *up, ssize id, cchar *event, cchar *data, void *arg)
{
Web *web = arg;
webWriteFmt(web, "id: %ld\nevent: %s\ndata: %s\n", id, event, data);
}
static void streamAction(Web *web)
{
Url *up = openaiStream(web->vars, (UrlSseProc) streamCallback, web);
if (up == NULL) {
webError(web, 500, "Cannot connect to OpenAI");
return;
}
urlFree(up);
webFinalize(web);
}Important Notes
- Never hardcode API keys in source — use
ioto.json5or environment variables. - The agent callback must return an
sclone()-allocated string (caller frees). openaiChatCompletion()andopenaiResponses()return allocatedJson *— caller mustjsonFree().- When
services.aiis enabled, Ioto initializes OpenAI automatically.
