{"_id":"@aiconnect/flue-openai-adapter","_rev":"2-258093616ad0741d1fc0c8959b2b82c2","name":"@aiconnect/flue-openai-adapter","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@aiconnect/flue-openai-adapter","version":"0.1.0","keywords":["flue","openai","chat-completions","hono","sse","streaming"],"license":"MIT","_id":"@aiconnect/flue-openai-adapter@0.1.0","maintainers":[{"name":"ericsantos","email":"ericsantos.net@gmail.com"}],"homepage":"https://github.com/aiconnect-cloud/flue-openai-adapter#readme","bugs":{"url":"https://github.com/aiconnect-cloud/flue-openai-adapter/issues"},"dist":{"shasum":"d7585dae1424c38b6c2d31b069e75d9628d3c10a","tarball":"https://registry.npmjs.org/@aiconnect/flue-openai-adapter/-/flue-openai-adapter-0.1.0.tgz","fileCount":19,"integrity":"sha512-tua6gndVGkTRM7WiAI4XaHGyeh2wqqsMoYALLJ48PWyZbKMeUWcfg+6w2oRhoniS4ikwvyL20E28Pwsa3NcGaw==","signatures":[{"sig":"MEUCIQDZUTIU62RcqjahEcnubBfktKxeHV9P0DfKyjRhdnlWlgIgfIaaEbCLw1bnKYTKu1ezcIi2qbC7cCmU9+yXHC+c9wA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":56918},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./drivers/agent-v2":{"types":"./dist/drivers/agent-v2.d.ts","import":"./dist/drivers/agent-v2.js"},"./drivers/workflow-v0":{"types":"./dist/drivers/workflow-v0.d.ts","import":"./dist/drivers/workflow-v0.js"}},"gitHead":"37a007298b4bce0a1550458f9c80461597ad9559","scripts":{"test":"vitest run","build":"tsup","release":"npm publish && git push --follow-tags","test:watch":"vitest","check:types":"tsc --noEmit","prepublishOnly":"npm run check:types && npm test && npm run build"},"_npmUser":{"name":"ericsantos","email":"ericsantos.net@gmail.com"},"repository":{"url":"git+https://github.com/aiconnect-cloud/flue-openai-adapter.git","type":"git"},"_npmVersion":"11.17.0","description":"OpenAI-compatible /v1/chat/completions route for Flue apps (Hono)","directories":{},"_nodeVersion":"26.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"hono":"^4.8.3","tsup":"^8.3.5","vitest":"^3.0.0","typescript":"^5.7.2"},"peerDependencies":{"hono":">=4"},"_npmOperationalInternal":{"tmp":"tmp/flue-openai-adapter_0.1.0_1786383505057_0.7880389830359986","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aiconnect/flue-openai-adapter","version":"0.2.0","description":"OpenAI-compatible /v1/chat/completions route for Flue apps (Hono)","license":"MIT","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./drivers/workflow-v0":{"types":"./dist/drivers/workflow-v0.d.ts","import":"./dist/drivers/workflow-v0.js"},"./drivers/agent-v2":{"types":"./dist/drivers/agent-v2.d.ts","import":"./dist/drivers/agent-v2.js"}},"publishConfig":{"access":"public"},"engines":{"node":">=20"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","check:types":"tsc --noEmit","prepublishOnly":"npm run check:types && npm test && npm run build","release":"npm publish && git push --follow-tags"},"peerDependencies":{"hono":">=4"},"devDependencies":{"hono":"^4.8.3","tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^3.0.0"},"repository":{"type":"git","url":"git+https://github.com/aiconnect-cloud/flue-openai-adapter.git"},"keywords":["flue","openai","chat-completions","hono","sse","streaming"],"gitHead":"4914c14936832e885c21d21f1235c7e05eb76e6a","_id":"@aiconnect/flue-openai-adapter@0.2.0","bugs":{"url":"https://github.com/aiconnect-cloud/flue-openai-adapter/issues"},"homepage":"https://github.com/aiconnect-cloud/flue-openai-adapter#readme","_nodeVersion":"26.5.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-caR6pYzZNZGf/DfxEBMCmGaD51R1fpS4G0TOyqQmv7sPemLm5LnGItDxLJMPmy3RCgrNywwwkRyDdzvMrK09eg==","shasum":"e9dcdae2bd7822049218b56b0f4fcc44418aeb0d","tarball":"https://registry.npmjs.org/@aiconnect/flue-openai-adapter/-/flue-openai-adapter-0.2.0.tgz","fileCount":19,"unpackedSize":63399,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDRUzSROpxPS2vPvMZvMxjo4F0ZHus2TJ70YZXOItH6WwIhAMLbgR/JQeRrAoO5pnJ558IQb6WRUYlGhNLPTgnkvjny"}]},"_npmUser":{"name":"ericsantos","email":"ericsantos.net@gmail.com"},"directories":{},"maintainers":[{"name":"ericsantos","email":"ericsantos.net@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/flue-openai-adapter_0.2.0_1786461988794_0.055902680861932064"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-10T17:38:24.930Z","modified":"2026-08-11T15:26:29.115Z","0.1.0":"2026-08-10T17:38:25.214Z","0.2.0":"2026-08-11T15:26:28.932Z"},"bugs":{"url":"https://github.com/aiconnect-cloud/flue-openai-adapter/issues"},"license":"MIT","homepage":"https://github.com/aiconnect-cloud/flue-openai-adapter#readme","keywords":["flue","openai","chat-completions","hono","sse","streaming"],"repository":{"type":"git","url":"git+https://github.com/aiconnect-cloud/flue-openai-adapter.git"},"description":"OpenAI-compatible /v1/chat/completions route for Flue apps (Hono)","maintainers":[{"name":"ericsantos","email":"ericsantos.net@gmail.com"}],"readme":"# @aiconnect/flue-openai-adapter\n\nOpenAI-compatible `POST /v1/chat/completions` route for [Flue](https://flue.dev) apps, as a mountable [Hono](https://hono.dev) sub-app. Supports streaming (SSE chunks) and non-streaming responses, with usage mapping and honest error signaling.\n\n## Install\n\n```sh\nnpm install @aiconnect/flue-openai-adapter\n```\n\n`hono >= 4` is a peer dependency. `@flue/runtime` is **not** imported — drivers talk to Flue over its HTTP/SSE surface, so one package works across Flue majors.\n\n## Usage (Flue 0.9.x workflow app)\n\n```ts\nimport { flue } from '@flue/runtime/routing';\nimport { Hono } from 'hono';\nimport { openaiCompat, workflowV0 } from '@aiconnect/flue-openai-adapter';\n\nconst app = new Hono();\n\napp.route('/v1', openaiCompat({\n  driver: workflowV0({\n    workflow: 'my-workflow',\n    app: () => app,\n  }),\n}));\n\napp.route('/', flue());\nexport default app;\n```\n\nThe driver self-dispatches `POST /workflows/<name>` through the app (no network hop) and relays the run's `text_delta` frames as OpenAI chunks.\n\n### Workflow contract (`streaming: 'deltas'`, default)\n\nThe target workflow must:\n\n- accept `stream: true` in its payload and synthesize **plain text** in that mode (structured output would stream raw JSON into the deltas);\n- return `{ answer?: string, usage?: { input, output, totalTokens } }` in its result, used by shortcut paths that produce no deltas (cache hits, static responses).\n\n### Workflows with structured output (`streaming: 'result-only'`)\n\n```ts\nworkflowV0({ workflow: 'my-workflow', app: () => app, streaming: 'result-only' })\n```\n\nNo `stream` flag is sent, deltas are ignored, and the answer comes exclusively from the final run result — streaming clients receive it as a single chunk.\n\n## Usage (Flue 2.x agent app)\n\n```ts\nimport { openaiCompat, agentV2 } from '@aiconnect/flue-openai-adapter';\n\napp.route('/v1', openaiCompat({\n  driver: agentV2({ agent: 'host', app: () => app }),\n}));\n```\n\nFlue 2 prompts are fire-and-forget: the driver POSTs `{ kind: 'user', body }` to `/agents/<name>/<id>` (202 + stream coordinates), reads the conversation stream (`GET ?view=updates&live=sse`), relays `message-delta` items with `kind: \"text\"` for the admitted submission (reasoning deltas are dropped), and aborts the stream once `submission-settled` arrives. A non-`completed` outcome (`failed`, `aborted`) is reported as an error.\n\nBy default each request runs in a fresh conversation (random UUID). Pass `conversation` to keep server-side history:\n\n```ts\nagentV2({ agent: 'host', app: () => app, conversation: (_q, req) => String(req.user ?? 'default') })\n```\n\n## Multi-agent mounts (`drivers`)\n\nOne `/v1` endpoint can front several agents: pass a `drivers` map and `request.model`\nselects the driver by key. `GET /v1/models` lists the keys (OpenAI list format), so\nmodel-picker UIs (Open WebUI, connect chat) discover the agents automatically.\n\n```ts\napp.route('/v1', openaiCompat({\n  drivers: {\n    sarah: agentV2({ agent: 'sarah', app: () => app }),\n    hello: agentV2({ agent: 'hello', app: () => app }),\n  },\n  defaultModel: 'hello', // lookup key when the request omits `model`\n}));\n```\n\nAn unknown `model` gets a 404 with `code: 'model_not_found'`. When the request omits\n`model`, `defaultModel` is used as the lookup key; if it is not a key and a single\n`driver` is also set, that driver handles the request (0.1.0 fallback). Single-driver\nmounts (`driver`) behave exactly as in 0.1.0, and `GET /models` then lists just\n`defaultModel`.\n\n## Options\n\n### `openaiCompat(options)`\n\n| Option | Default | Purpose |\n|---|---|---|\n| `driver` | — | Single transport adapter; required unless `drivers` is set |\n| `drivers` | — | `model` → driver map; keys become `GET /models` entries |\n| `resolveAnswer(result)` | `result.answer ?? ''` | Assistant content for runs without deltas |\n| `extractQuestion(messages)` | last `user` message with string content | Question sent to the driver |\n| `defaultModel` | `'flue'` | Model echoed when the request omits `model`; with `drivers`, also its lookup key |\n\n### `workflowV0(options)`\n\n| Option | Default | Purpose |\n|---|---|---|\n| `workflow` | — | Workflow name (`/workflows/<name>`) |\n| `app` | — | Lazy ref to the Hono app for self-dispatch |\n| `streaming` | `'deltas'` | `'deltas'` \\| `'result-only'` |\n| `buildPayload(question, request)` | `{ question, stream? }` | Custom workflow payload |\n| `baseUrl` | `http://flue.internal` | Internal dispatch base URL |\n\n### `agentV2(options)`\n\n| Option | Default | Purpose |\n|---|---|---|\n| `agent` | — | Agent route mount name (`/agents/<name>`) |\n| `app` | global fetch | Lazy ref to the Hono app; omit to reach an external server via `baseUrl` |\n| `baseUrl` | `http://flue.internal` | Base URL for admission/stream requests |\n| `conversation(question, request)` | random UUID | Conversation instance id (stable id keeps history) |\n\n## Error semantics\n\n- Driver invocation failure → HTTP 502 with `{ error: { type: 'upstream_error' } }`.\n- Errored run, non-streaming → HTTP 502, even if partial text accumulated.\n- Errored run, streaming → a top-level `data: { \"error\": ... }` SSE payload (the OpenAI wire convention; `finish_reason` is never `\"stop\"` on an errored run), then `data: [DONE]`.\n- A stream that closes without its terminal frame (`run_end` / `submission-settled`) is treated as truncated and reported as an error, never as a clean completion.\n- Client disconnect on a streaming response aborts the underlying Flue run (`cancel()` → driver `abort()`).\n\n## Usage accounting\n\nNon-streaming responses carry `usage` mapped from the driver result. Streaming responses include a final usage chunk (empty `choices`) only when the client sends `stream_options: { include_usage: true }`, matching OpenAI behavior.\n\n## Custom drivers\n\nImplement the `Driver` interface to adapt other transports:\n\n```ts\ninterface Driver {\n  run(question: string, request: ChatCompletionRequest): Promise<AsyncIterable<DriverEvent>>;\n}\n// DriverEvent = { type: 'delta', text } | { type: 'end', result, errored }\n```\n\n## Publishing\n\n```sh\nnpm version patch   # ou minor / major — cria o commit e a tag\nnpm run release     # prepublishOnly (types + testes + build) → publish → push da tag\n```\n\n## Roadmap\n\n- `workflowV0` is transitional: it will be removed in 1.0 once consumers migrate to Flue 2.\n- `usage` mapping for `agentV2` (the Flue 2 conversation stream does not expose token usage; reported as zeros).\n\n## Limitations\n\n- Only the last `user` message with text is used (override with `extractQuestion`); conversation history is ignored. Array-of-parts content is supported — its `text` parts are joined; image/audio parts are ignored.\n- One choice per response (`n` is not supported).\n","readmeFilename":"README.md"}