Skip to content

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.json5 or environment variables.
  • The agent callback must return an sclone()-allocated string (caller frees).
  • openaiChatCompletion() and openaiResponses() return allocated Json * — caller must jsonFree().
  • When services.ai is enabled, Ioto initializes OpenAI automatically.