From 16b59b3e51257bdc1d36d87c50e047ff4df95857 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:22:15 -0700 Subject: [PATCH 01/27] Add Completion Endpoint --- src/index.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 2379805..b0c9e02 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,7 +61,7 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -app.post('/', async (req: Request, res: Response) => { +app.post(['/', '/chat'], async (req: Request, res: Response) => { // console.log('req.headers:', req.headers) // console.log('authorization:', req.headers.authorization) const { messages, system } = req.body @@ -84,6 +84,30 @@ app.post('/', async (req: Request, res: Response) => { pipeUIMessageStreamToResponse({ response: res, stream }) }) +app.post('/completion', async (req: Request, res: Response) => { + const { prompt, system } = req.body + console.log('prompt:', prompt.length, 'system:', system.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.COMPLETION_INSTRUCTIONS, + maxOutputTokens, + providerOptions, + onEnd({ finishReason, finalStep, text, usage }) { + console.log('finishReason:', finishReason) + console.log('usage:', usage) + console.log('reasoning:', finalStep.reasoningText) + console.log('response:', text) + }, + }) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + writer.merge(toUIMessageStream({ stream: result.stream })) + }, + }) + pipeUIMessageStreamToResponse({ response: res, stream }) +}) + function corsCallback( origin: string | undefined, callback: (err: Error | null, origin?: boolean) => void, From 5161b933dd16ecd4b30be084d0f7f24dd255069c Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:36:49 -0700 Subject: [PATCH 02/27] Cleanup --- src/index.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index b0c9e02..b7c8ac4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { + consumeStream, streamText, createUIMessageStream, pipeUIMessageStreamToResponse, @@ -85,19 +86,23 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { }) app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body - console.log('prompt:', prompt.length, 'system:', system.length) + const { prompt, system, signal } = req.body + console.log('prompt:', prompt?.length, 'system:', system?.length) const result = streamText({ model: model, prompt, system: system || process.env.COMPLETION_INSTRUCTIONS, maxOutputTokens, providerOptions, - onEnd({ finishReason, finalStep, text, usage }) { - console.log('finishReason:', finishReason) - console.log('usage:', usage) + abortSignal: signal, + onError({ error }) { + console.log('error:', error) + }, + onEnd({ finalStep, finishReason, text, usage }) { console.log('reasoning:', finalStep.reasoningText) console.log('response:', text) + console.log('usage:', usage) + console.log('finishReason:', finishReason) }, }) const stream = createUIMessageStream({ @@ -105,7 +110,11 @@ app.post('/completion', async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - pipeUIMessageStreamToResponse({ response: res, stream }) + pipeUIMessageStreamToResponse({ + response: res, + stream, + consumeSseStream: consumeStream, + }) }) function corsCallback( From 091be2300fa2e01606e58a86f24065e48c2cf7db Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Tue, 14 Jul 2026 21:57:42 -0700 Subject: [PATCH 03/27] Add /object --- src/index.ts | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index b7c8ac4..7bd2e39 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,11 +7,14 @@ import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { + Output, consumeStream, + pipeTextStreamToResponse, streamText, createUIMessageStream, pipeUIMessageStreamToResponse, convertToModelMessages, + toTextStream, toUIMessageStream, } from 'ai' @@ -43,7 +46,11 @@ const maxOutputTokens = process.env.MAX_TOKENS ? Number.parseInt(process.env.MAX_TOKENS) : undefined console.log(`maxOutputTokens: ${maxOutputTokens}`) -console.log(`INSTRUCTIONS: ${process.env.INSTRUCTIONS}`) +console.log( + `CHAT_INSTRUCTIONS: ${process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS}`, // NOSONAR +) +console.log(`COMPLETION_INSTRUCTIONS: ${process.env.COMPLETION_INSTRUCTIONS}`) +console.log(`OBJECT_INSTRUCTIONS: ${process.env.OBJECT_INSTRUCTIONS}`) const model = getModel() console.log(`Loaded modelId: ${model.modelId}`) @@ -74,7 +81,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - system: system || process.env.INSTRUCTIONS, + system: system || process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS, maxOutputTokens, providerOptions, }) @@ -86,7 +93,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { }) app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system, signal } = req.body + const { prompt, system } = req.body console.log('prompt:', prompt?.length, 'system:', system?.length) const result = streamText({ model: model, @@ -94,8 +101,7 @@ app.post('/completion', async (req: Request, res: Response) => { system: system || process.env.COMPLETION_INSTRUCTIONS, maxOutputTokens, providerOptions, - abortSignal: signal, - onError({ error }) { + onError(error) { console.log('error:', error) }, onEnd({ finalStep, finishReason, text, usage }) { @@ -117,6 +123,32 @@ app.post('/completion', async (req: Request, res: Response) => { }) }) +app.post('/object', async (req: Request, res: Response) => { + const { outputSchema, prompt, system } = req.body + console.log('prompt:', prompt?.length, 'system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.OBJECT_INSTRUCTIONS, + maxOutputTokens, + providerOptions, + output: outputSchema ? Output.object({ schema: outputSchema }) : Output.json(), + onError(error) { + console.log('error:', error) + }, + onEnd({ finalStep, finishReason, text, usage }) { + console.log('reasoning:', finalStep.reasoningText) + console.log('response:', text) + console.log('usage:', usage) + console.log('finishReason:', finishReason) + }, + }) + pipeTextStreamToResponse({ + response: res, + stream: toTextStream({ stream: result.stream }), + }) +}) + function corsCallback( origin: string | undefined, callback: (err: Error | null, origin?: boolean) => void, From 23f9abb19470270fd95bcef0176eb61ae183bbd4 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:47:40 -0700 Subject: [PATCH 04/27] Update README.md --- README.md | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---- src/index.ts | 9 ++++++--- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 47ed6ad..0578466 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ To get started [Setup](#setup) and [Configure](#configure) the server. No API Ke [![View Live Demo](https://img.shields.io/badge/view_live_demo-green?style=for-the-badge&logo=chatbot&logoColor=white)](https://cssnr.github.io/vitepress-chat/) -- Client: https://github.com/cssnr/vitepress-chat -- Server: https://github.com/cssnr/chat-server +- Client: +- Server: ### Features - Works with Claude, OpenAI, Gemini and OpenAI Compatible Providers +- Chat, Completion, and Object Endpoints - Live Stream Results to Client - Automatic Input Token Caching - Automatic Retry on API Failures @@ -107,7 +108,9 @@ Environment Variables. | `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | | [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | `MAX_TOKENS` | - | Max Output Tokens | -| `INSTRUCTIONS` | - | Fallback System Instructions | +| `CHAT_INSTRUCTIONS` | - | System Instructions for Chat | +| `COMPLETION_INSTRUCTIONS` | - | System Instructions for Completion | +| `OBJECT_INSTRUCTIONS` | - | System Instructions for Object | | `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | @@ -145,6 +148,16 @@ The value is only checked for valid JSON at startup and will fail at runtime if ## Client +### Endpoints + +| Endpoint | Method | Description | +| :------------ | :----: | :------------------------------------------------------------------------------------------------------------------------------------- | +| `/chat` | `POST` | Use with [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) and [VitePress Chat](https://cssnr.github.io/vitepress-chat/) | +| `/completion` | `POST` | Use with [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| `/object` | `POST` | Use with [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | + +### Chat + To send System Instructions from the client, add them to the body. ```typescript @@ -157,11 +170,46 @@ const chat = new Chat({ }) ``` +Reference: + +### Completion + +```typescript +import { useCompletion } from '@ai-sdk/react' + +const { completion, complete, isLoading, stop } = useCompletion({ + api: 'https://chat-server.cssnr.com/completion', + headers: { Authorization: 'Basic Abc123=' }, + body: { system: 'You are a helpful assistant.' }, +}) +``` + +Reference: + +### Object + +```typescript +import { useObject } from '@ai-sdk/react' + +const { object, submit } = useObject({ + api: 'https://chat-server.cssnr.com/object', + schema: z.object({ name: z.string(), age: z.number() }), + headers: { Authorization: 'Basic Abc123=' }, +}) + +submit({ + system: 'You are a helpful assistant.', + prompt: 'Extract the name and age from: John is 30 years old.', +}) +``` + +Reference: + ### VitePress Chat Plugin The client is currently available as a VitePress Plugin. -- https://github.com/cssnr/vitepress-chat +- [![View Documentation](https://img.shields.io/badge/view_documentation-blue?style=for-the-badge&logo=googledocs&logoColor=white)](https://cssnr.github.io/vitepress-chat/) diff --git a/src/index.ts b/src/index.ts index 7bd2e39..7ea1dc1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -73,7 +73,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { // console.log('req.headers:', req.headers) // console.log('authorization:', req.headers.authorization) const { messages, system } = req.body - // if (system) console.log('system:', system.substring(0, 512)) + // console.log('system:', system?.substring(0, 512)) const modelMessages = await convertToModelMessages(messages) console.log('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ @@ -94,7 +94,8 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { app.post('/completion', async (req: Request, res: Response) => { const { prompt, system } = req.body - console.log('prompt:', prompt?.length, 'system:', system?.length) + console.log('prompt:', prompt?.length) + console.log('system:', system?.length) const result = streamText({ model: model, prompt, @@ -125,7 +126,9 @@ app.post('/completion', async (req: Request, res: Response) => { app.post('/object', async (req: Request, res: Response) => { const { outputSchema, prompt, system } = req.body - console.log('prompt:', prompt?.length, 'system:', system?.length) + console.log('outputSchema:', outputSchema?.length) + console.log('prompt:', prompt?.length) + console.log('system:', system?.length) const result = streamText({ model: model, prompt, From d63e304a1e4ec14775751595f9ac5c33548e19a2 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:45:52 -0700 Subject: [PATCH 05/27] Updates --- README.md | 6 +++++- src/index.ts | 7 ++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0578466..7d0df3b 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ To send System Instructions from the client, add them to the body. ```typescript const chat = new Chat({ transport: new DefaultChatTransport({ - api: 'https://chat-server.cssnr.com/', + api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, body: { system: 'You are a helpful assistant.' }, }), @@ -200,6 +200,10 @@ const { object, submit } = useObject({ submit({ system: 'You are a helpful assistant.', prompt: 'Extract the name and age from: John is 30 years old.', + output: { + type: 'object', + properties: { name: { type: 'string' }, age: { type: 'number' } }, + }, }) ``` diff --git a/src/index.ts b/src/index.ts index 7ea1dc1..fe505b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { Output, consumeStream, + jsonSchema, pipeTextStreamToResponse, streamText, createUIMessageStream, @@ -125,8 +126,8 @@ app.post('/completion', async (req: Request, res: Response) => { }) app.post('/object', async (req: Request, res: Response) => { - const { outputSchema, prompt, system } = req.body - console.log('outputSchema:', outputSchema?.length) + const { output, prompt, system } = req.body + console.log('output:', output ? 'SET' : undefined) console.log('prompt:', prompt?.length) console.log('system:', system?.length) const result = streamText({ @@ -135,7 +136,7 @@ app.post('/object', async (req: Request, res: Response) => { system: system || process.env.OBJECT_INSTRUCTIONS, maxOutputTokens, providerOptions, - output: outputSchema ? Output.object({ schema: outputSchema }) : Output.json(), + output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), onError(error) { console.log('error:', error) }, From e641616498074ef0ae70a8ff99111f4ce304ca90 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Wed, 15 Jul 2026 22:33:01 -0700 Subject: [PATCH 06/27] Cleanup --- src/index.ts | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/index.ts b/src/index.ts index fe505b8..6e16701 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,14 +7,15 @@ import { google } from '@ai-sdk/google' import { openai } from '@ai-sdk/openai' import { createOpenAICompatible } from '@ai-sdk/openai-compatible' import { + type GenerateTextEndEvent, Output, consumeStream, + convertToModelMessages, + createUIMessageStream, jsonSchema, pipeTextStreamToResponse, - streamText, - createUIMessageStream, pipeUIMessageStreamToResponse, - convertToModelMessages, + streamText, toTextStream, toUIMessageStream, } from 'ai' @@ -76,7 +77,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const { messages, system } = req.body // console.log('system:', system?.substring(0, 512)) const modelMessages = await convertToModelMessages(messages) - console.log('modelMessages:', modelMessages.length) + // console.log('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ execute: ({ writer }) => { const result = streamText({ @@ -85,6 +86,8 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { system: system || process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS, maxOutputTokens, providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, }) writer.merge(toUIMessageStream({ stream: result.stream })) }, @@ -103,15 +106,8 @@ app.post('/completion', async (req: Request, res: Response) => { system: system || process.env.COMPLETION_INSTRUCTIONS, maxOutputTokens, providerOptions, - onError(error) { - console.log('error:', error) - }, - onEnd({ finalStep, finishReason, text, usage }) { - console.log('reasoning:', finalStep.reasoningText) - console.log('response:', text) - console.log('usage:', usage) - console.log('finishReason:', finishReason) - }, + onError: onStreamError, + onEnd: onStreamEnd, }) const stream = createUIMessageStream({ execute: ({ writer }) => { @@ -137,15 +133,8 @@ app.post('/object', async (req: Request, res: Response) => { maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), - onError(error) { - console.log('error:', error) - }, - onEnd({ finalStep, finishReason, text, usage }) { - console.log('reasoning:', finalStep.reasoningText) - console.log('response:', text) - console.log('usage:', usage) - console.log('finishReason:', finishReason) - }, + onError: onStreamError, + onEnd: onStreamEnd, }) pipeTextStreamToResponse({ response: res, @@ -195,3 +184,14 @@ function getProviderOptions() { console.error('parsing PROVIDER_OPTIONS as JSON') } } + +function onStreamError(error: unknown) { + console.log('error:', error) +} + +function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { + console.log('reasoning:', finalStep.reasoningText) + console.log('response:', text) + console.log('usage:', usage) + console.log('finishReason:', finishReason) +} From 218b7f4cf9bb7bacbeaefee7cd6f20faf469ca51 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 11:45:33 -0700 Subject: [PATCH 07/27] Add debug --- README.md | 1 + package-lock.json | 19 +++++++++++++++++++ package.json | 2 ++ src/index.ts | 37 +++++++++++++++++++++---------------- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7d0df3b..8d6e7cf 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Environment Variables. | `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | +| `DEBUG` | - | Set to `app` for debug logs | You must also set the API key for the `MODEL` you select. diff --git a/package-lock.json b/package-lock.json index f49f933..6a33da0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@ai-sdk/openai-compatible": "^3.0.2", "ai": "^7.0.8", "cors": "^2.8.6", + "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", "picomatch": "^4.0.4" @@ -21,6 +22,7 @@ "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", + "@types/debug": "^4.1.13", "@types/express": "^5.0.6", "@types/node": "^26.0.1", "@types/picomatch": "^4.0.3", @@ -816,6 +818,16 @@ "@types/node": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -869,6 +881,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", diff --git a/package.json b/package.json index 8ab8f5a..6a81fcc 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@ai-sdk/openai-compatible": "^3.0.2", "ai": "^7.0.8", "cors": "^2.8.6", + "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", "picomatch": "^4.0.4" @@ -29,6 +30,7 @@ "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", + "@types/debug": "^4.1.13", "@types/express": "^5.0.6", "@types/node": "^26.0.1", "@types/picomatch": "^4.0.3", diff --git a/src/index.ts b/src/index.ts index 6e16701..feb0cad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import createDebug from 'debug' import cors from 'cors' import dotenv from 'dotenv' import pm from 'picomatch' @@ -22,9 +23,14 @@ import { dotenv.config({ path: 'settings.env' }) +console.log(`DEBUG: ${process.env.DEBUG}`) +createDebug.enable(process.env.DEBUG ?? '') +const debug = createDebug('app') +debug('debug enabled: app') + console.log(`chat-server: ${process.env.APP_VERSION}`) -console.log('MODEL:', process.env.MODEL ? 'SET' : undefined) +console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY ? 'SET' : undefined) console.log( @@ -72,12 +78,12 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) app.post(['/', '/chat'], async (req: Request, res: Response) => { - // console.log('req.headers:', req.headers) - // console.log('authorization:', req.headers.authorization) + // debug('req.headers:', req.headers) + // debug('authorization:', req.headers.authorization) const { messages, system } = req.body - // console.log('system:', system?.substring(0, 512)) + // debug('system:', system?.substring(0, 128)) const modelMessages = await convertToModelMessages(messages) - // console.log('modelMessages:', modelMessages.length) + debug('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ execute: ({ writer }) => { const result = streamText({ @@ -92,14 +98,13 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - // console.log('stream:', stream) pipeUIMessageStreamToResponse({ response: res, stream }) }) app.post('/completion', async (req: Request, res: Response) => { const { prompt, system } = req.body - console.log('prompt:', prompt?.length) - console.log('system:', system?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) const result = streamText({ model: model, prompt, @@ -123,9 +128,9 @@ app.post('/completion', async (req: Request, res: Response) => { app.post('/object', async (req: Request, res: Response) => { const { output, prompt, system } = req.body - console.log('output:', output ? 'SET' : undefined) - console.log('prompt:', prompt?.length) - console.log('system:', system?.length) + debug('output:', output?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) const result = streamText({ model: model, prompt, @@ -186,12 +191,12 @@ function getProviderOptions() { } function onStreamError(error: unknown) { - console.log('error:', error) + console.error('error:', error) } function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { - console.log('reasoning:', finalStep.reasoningText) - console.log('response:', text) - console.log('usage:', usage) - console.log('finishReason:', finishReason) + debug('reasoning:', finalStep.reasoningText) + debug('response:', text) + debug('usage:', usage) + debug('finishReason:', finishReason) } From 078e13c5d9dd0d68acba727c83064d46a93861e8 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:26:42 -0700 Subject: [PATCH 08/27] Update README.md --- README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8d6e7cf..903b09f 100644 --- a/README.md +++ b/README.md @@ -48,12 +48,12 @@ To get started [Setup](#setup) and [Configure](#configure) the server. No API Ke ### Features - Works with Claude, OpenAI, Gemini and OpenAI Compatible Providers -- Chat, Completion, and Object Endpoints -- Live Stream Results to Client +- Includes Chat, Completion, and Object Endpoints +- Supports Multiple Clients Simultaneously +- Live Streams the Results to the Client - Automatic Input Token Caching - Automatic Retry on API Failures - Deploy with Docker or Node -- Supports Multiple Clients Simultaneously - Plus all the [Client Features](https://github.com/cssnr/vitepress-chat?tab=readme-ov-file#features) Built with the [AI SDK](https://ai-sdk.dev/). @@ -116,6 +116,8 @@ Environment Variables. | `PORT` | `3000` | Server Port | | `DEBUG` | - | Set to `app` for debug logs | +Note: The `INSTRUCTIONS` variable also points to the `CHAT_INSTRUCTIONS` variable (recommended). + You must also set the API key for the `MODEL` you select. | Variable | Description | @@ -157,12 +159,17 @@ The value is only checked for valid JSON at startup and will fail at runtime if | `/completion` | `POST` | Use with [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | | `/object` | `POST` | Use with [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | +Note: The `/` endpoint also points to the `/chat` endpoint (recommended). + ### Chat To send System Instructions from the client, add them to the body. ```typescript -const chat = new Chat({ +import { useChat } from '@ai-sdk/vue' +import { DefaultChatTransport } from 'ai' + +const { messages, input, handleSubmit } = useChat({ transport: new DefaultChatTransport({ api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, @@ -176,7 +183,7 @@ Reference: ### Completion ```typescript -import { useCompletion } from '@ai-sdk/react' +import { useCompletion } from '@ai-sdk/vue' const { completion, complete, isLoading, stop } = useCompletion({ api: 'https://chat-server.cssnr.com/completion', @@ -190,7 +197,7 @@ Reference: ### Object ```typescript -import { useObject } from '@ai-sdk/react' +import { useObject } from '@ai-sdk/vue' const { object, submit } = useObject({ api: 'https://chat-server.cssnr.com/object', From 9cbea121307eecd30d7bd4e2adc1cec071a8c270 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:43:32 -0700 Subject: [PATCH 09/27] Update README.md --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 903b09f..73ee0b8 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ For a Portainer Deploy workflow see the [.github/workflows/deploy.yaml](https:// 💡 All variables are optional. The default `big-pickle` model works with NO API Key. -Environment Variables. +Environment Variables (can be placed in a `settings.env` file). | Variable | Default | Description | | :------------------------------------ | :--------------------------- | :---------------------------------- | @@ -161,7 +161,7 @@ The value is only checked for valid JSON at startup and will fail at runtime if Note: The `/` endpoint also points to the `/chat` endpoint (recommended). -### Chat +#### chat To send System Instructions from the client, add them to the body. @@ -180,7 +180,7 @@ const { messages, input, handleSubmit } = useChat({ Reference: -### Completion +#### completion ```typescript import { useCompletion } from '@ai-sdk/vue' @@ -194,7 +194,7 @@ const { completion, complete, isLoading, stop } = useCompletion({ Reference: -### Object +#### object ```typescript import { useObject } from '@ai-sdk/vue' @@ -215,6 +215,8 @@ submit({ }) ``` +Note: Both `system` and `output` are custom body parameters parsed by the server allowing the client to send these items. + Reference: ### VitePress Chat Plugin @@ -227,6 +229,8 @@ The client is currently available as a VitePress Plugin. ## Development +To enable debug logs set: `DEBUG=app` + This works with no configuration using the `big-pickle` model. You can set your environment variables in the `settings.env` file. If using `big-pickle` for testing it is much faster to disable reasoning. From 248c88bb325b59873b90a40ab33e5bed3c9fa71f Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:45:10 -0700 Subject: [PATCH 10/27] Update Dependencies and Workflows --- .github/workflows/build.yaml | 2 +- .github/workflows/deploy.yaml | 2 +- .github/workflows/draft.yaml | 2 +- .github/workflows/issue.yaml | 2 +- .github/workflows/labeler.yaml | 2 +- .github/workflows/lint.yaml | 4 +- package-lock.json | 268 ++++++++++++++++----------------- package.json | 22 +-- src/index.ts | 1 + 9 files changed, 153 insertions(+), 152 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fb2c838..b32ad09 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,7 +32,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Debug event.json" continue-on-error: true diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index c9c3c80..9d20930 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -35,7 +35,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Debug event.json" continue-on-error: true diff --git a/.github/workflows/draft.yaml b/.github/workflows/draft.yaml index 7f93c65..9c43df3 100644 --- a/.github/workflows/draft.yaml +++ b/.github/workflows/draft.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Draft Release Action" id: draft diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 1b1d2ae..e0bcf6e 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -17,7 +17,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Create App Token" id: app diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 95030e5..922b063 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: "Checkout Configs" - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: cssnr/configs ref: master diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 334bc85..5ea343e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: "Checkout" - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: "Debug event.json" continue-on-error: true @@ -31,7 +31,7 @@ jobs: run: echo "$GITHUB_CTX" - name: "Setup Node" - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: 24 diff --git a/package-lock.json b/package-lock.json index 6a33da0..2d7de4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,40 +8,40 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.3", - "@ai-sdk/google": "^4.0.3", - "@ai-sdk/openai": "^4.0.4", - "@ai-sdk/openai-compatible": "^3.0.2", - "ai": "^7.0.8", + "@ai-sdk/anthropic": "^4.0.15", + "@ai-sdk/google": "^4.0.17", + "@ai-sdk/openai": "^4.0.15", + "@ai-sdk/openai-compatible": "^3.0.11", + "ai": "^7.0.29", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", - "picomatch": "^4.0.4" + "picomatch": "^4.0.5" }, "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.0.1", + "@types/node": "^26.1.1", "@types/picomatch": "^4.0.3", - "eslint": "^10.6.0", + "eslint": "^10.7.0", "nodemon": "^3.1.14", - "prettier": "^3.9.4", - "tsx": "^4.22.4", + "prettier": "^3.9.5", + "tsx": "^4.23.1", "typescript": "^6.0.3", - "typescript-eslint": "^8.62.1" + "typescript-eslint": "^8.64.0" } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.3.tgz", - "integrity": "sha512-USlJtQxkpbZy01evFpAOOtkELwDoWFlLrlJDM7B81KTqGVIZ60BMoEfJHZ/G4poK+t7tYFpHgDKalspc4TJKeg==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.15.tgz", + "integrity": "sha512-6iVlzqZjqqoHTmgO1WU9id/33pYykIRasWJFAMf1+d+nhju6d7566VRFsOGZ2W2GzgNtzKs8DGsYN7X5/wOGuw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.6.tgz", - "integrity": "sha512-8GjssUxXeTd8tst2fYXNOFbtNNZFv3sG7aq7wKnQYrU2eB3JFR7np6o4F3Snp/fy/rJZSeH28LvjSWq5wkdL8Q==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.21.tgz", + "integrity": "sha512-GafyeyHFDKJjdtbyhCQ0aC2FORdyE56IxK45EZ1JLO1iVdg8XqEU3bwnRzI0+5S1XHV7W2qQsxkZnGYPbsFiXA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2", + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.3.tgz", - "integrity": "sha512-mTTQt9/+/traTwrM4+J3Ewea1WgQBHJC+3EVa3dqdOfN1iyOC9P76CpGCZdUyVTASfVCpUTKrgCxHUTl9S7q/A==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.17.tgz", + "integrity": "sha512-90YMrWWIz7HVqrtthaP60fqGhsTT8OXHskxJ9sPmD/bFBHqdbOBrtS9oKBWjO0XBAY8eo2Rd7Ai8zGKCTvPn9Q==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.4.tgz", - "integrity": "sha512-p6VDEzEx52PIzRnFoUpiw6ABn07h4Rt+atL+M51W9SqwhselaZFRnz/pzv7LF9D1Gok5qfzOmR+JwvDDvDWS3w==", + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.15.tgz", + "integrity": "sha512-JpTLQp5RUbRcs5nOyPEu5NRdxZLUnD/uCyT3qzy26D+iunCeL7KJV58ER9kwisAKnTjWravfNblaQNiWr20M9A==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.2.tgz", - "integrity": "sha512-C6qUMe+qSMn7HVr1a0v5H9BXYKhsT5uO4Q2VYVHYdT7johzx+d1gYgFMhkRIoheBpCl5NPhljR4I4sHMWWfz6w==", + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.11.tgz", + "integrity": "sha512-PKLyt5PfjV2maWJPZpm3W29vCc+BbyqSPAcghXhl+iIxzFNjpGPa6UD/8J0b9l7w/lJCZKuhcef9q+Gp5WqUvw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -116,9 +116,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.1.tgz", - "integrity": "sha512-6p3C/vGqVIjcptBu1DnVd/BZJ2wWmV9TUv9192vT6ZvT9KNED8EwRTqyqFpoQZKgSbMDSvBSq3dqR524Nt/Crw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.3.tgz", + "integrity": "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -128,12 +128,12 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.2.tgz", - "integrity": "sha512-EcmdjJb7yggsZPCbS3MFBpvAUnKaPW+QvanU5GzF00XCq0bqqAmvJ3MN19ejlmOETbW8sJNiq6qam48wTcbUNw==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.10.tgz", + "integrity": "sha512-uPyec0+85dwxZYXtb8qe8gCjhjDfxP4LCDo/uRQS/iG+FIgYbHPRhr/ys281udG90bTaE18+5cxWraYaf8oHCw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.1", + "@ai-sdk/provider": "4.0.3", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8" @@ -855,9 +855,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", + "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", "dev": true, "license": "MIT", "dependencies": { @@ -889,9 +889,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", - "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "dev": true, "license": "MIT", "dependencies": { @@ -941,17 +941,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz", - "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/type-utils": "8.62.1", - "@typescript-eslint/utils": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -964,15 +964,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.62.1", + "@typescript-eslint/parser": "^8.64.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -980,16 +980,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz", - "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "debug": "^4.4.3" }, "engines": { @@ -1005,14 +1005,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz", - "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.62.1", - "@typescript-eslint/types": "^8.62.1", + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", "debug": "^4.4.3" }, "engines": { @@ -1027,14 +1027,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz", - "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1045,9 +1045,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz", - "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { @@ -1062,15 +1062,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz", - "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/utils": "8.62.1", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1087,9 +1087,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz", - "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { @@ -1101,16 +1101,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz", - "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.62.1", - "@typescript-eslint/tsconfig-utils": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/visitor-keys": "8.62.1", + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1129,16 +1129,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz", - "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.62.1", - "@typescript-eslint/types": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1" + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1153,13 +1153,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz", - "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/types": "8.64.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1222,14 +1222,14 @@ } }, "node_modules/ai": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.8.tgz", - "integrity": "sha512-vTEKl6fDBZ2IxBXTRaZOajf9W2Ev57Ju8iKtUvqlmDk8Z9BrEP4c22SWJsg1RcWHSFmJMSBa/s5dlUBHUq3YwA==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.29.tgz", + "integrity": "sha512-q+A+skhl6SyjWliU6W7zNYgDUrEk5SbNrCc5vt4S9n6+n6e7jSorDmu51hlhjDOsS7VwzWGCgbWFvvT3iomtvg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.6", - "@ai-sdk/provider": "4.0.1", - "@ai-sdk/provider-utils": "5.0.2" + "@ai-sdk/gateway": "4.0.21", + "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider-utils": "5.0.10" }, "engines": { "node": ">=22" @@ -1682,9 +1682,9 @@ } }, "node_modules/eslint": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", - "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", + "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -2180,9 +2180,9 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -2627,9 +2627,9 @@ } }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -2649,9 +2649,9 @@ } }, "node_modules/prettier": { - "version": "3.9.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.4.tgz", - "integrity": "sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==", + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", + "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", "dev": true, "license": "MIT", "bin": { @@ -3043,9 +3043,9 @@ } }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", + "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3120,16 +3120,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.62.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.1.tgz", - "integrity": "sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", + "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.62.1", - "@typescript-eslint/parser": "8.62.1", - "@typescript-eslint/typescript-estree": "8.62.1", - "@typescript-eslint/utils": "8.62.1" + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 6a81fcc..771f95e 100644 --- a/package.json +++ b/package.json @@ -16,29 +16,29 @@ "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.3", - "@ai-sdk/google": "^4.0.3", - "@ai-sdk/openai": "^4.0.4", - "@ai-sdk/openai-compatible": "^3.0.2", - "ai": "^7.0.8", + "@ai-sdk/anthropic": "^4.0.15", + "@ai-sdk/google": "^4.0.17", + "@ai-sdk/openai": "^4.0.15", + "@ai-sdk/openai-compatible": "^3.0.11", + "ai": "^7.0.29", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", "express": "^5.2.1", - "picomatch": "^4.0.4" + "picomatch": "^4.0.5" }, "devDependencies": { "@eslint/js": "^10.0.1", "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.0.1", + "@types/node": "^26.1.1", "@types/picomatch": "^4.0.3", - "eslint": "^10.6.0", + "eslint": "^10.7.0", "nodemon": "^3.1.14", - "prettier": "^3.9.4", - "tsx": "^4.22.4", + "prettier": "^3.9.5", + "tsx": "^4.23.1", "typescript": "^6.0.3", - "typescript-eslint": "^8.62.1" + "typescript-eslint": "^8.64.0" } } diff --git a/src/index.ts b/src/index.ts index feb0cad..bb805e7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -81,6 +81,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { // debug('req.headers:', req.headers) // debug('authorization:', req.headers.authorization) const { messages, system } = req.body + debug('system:', system?.length) // debug('system:', system?.substring(0, 128)) const modelMessages = await convertToModelMessages(messages) debug('modelMessages:', modelMessages.length) From f8d1d69f0189b98c452283662006654d5eeb7279 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 19:59:48 -0700 Subject: [PATCH 11/27] Add Disable Endpoint Options --- AGENTS.md | 8 +++ README.md | 11 ++-- src/index.ts | 150 +++++++++++++++++++++++++++------------------------ 3 files changed, 95 insertions(+), 74 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bb46bf5..c8cbb07 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,3 +15,11 @@ ALWAYS use the `npm run *` command | `npm run lint` | ESLint on `src/` | | `npm run tsc` | TypeScript Check Only `tsc --noEmit` | | `npm run prettier` | ALWAYS RUN AFTER EDITING FILES | + +## Endpoints + +| Endpoint | Links | +| :------------ | :-------------------------------------------------------------------------------------- | +| `/chat` | Client Docs [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | +| `/completion` | Client Docs [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| `/object` | Client Docs [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | diff --git a/README.md b/README.md index 73ee0b8..7789a17 100644 --- a/README.md +++ b/README.md @@ -108,15 +108,18 @@ Environment Variables (can be placed in a `settings.env` file). | `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | | [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | `MAX_TOKENS` | - | Max Output Tokens | -| `CHAT_INSTRUCTIONS` | - | System Instructions for Chat | -| `COMPLETION_INSTRUCTIONS` | - | System Instructions for Completion | -| `OBJECT_INSTRUCTIONS` | - | System Instructions for Object | +| `INSTRUCTIONS_CHAT` | - | System Instructions for Chat | +| `INSTRUCTIONS_COMPLETION` | - | System Instructions for Completion | +| `INSTRUCTIONS_OBJECT` | - | System Instructions for Object | +| `DISABLE_CHAT` | - | Disable the `/chat` endpoint | +| `DISABLE_COMPLETION` | - | Disable the `/completion` endpoint | +| `DISABLE_OBJECT` | - | Disable the `/object` endpoint | | `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | | `DEBUG` | - | Set to `app` for debug logs | -Note: The `INSTRUCTIONS` variable also points to the `CHAT_INSTRUCTIONS` variable (recommended). +Note: The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). You must also set the API key for the `MODEL` you select. diff --git a/src/index.ts b/src/index.ts index bb805e7..5d080d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,10 @@ debug('debug enabled: app') console.log(`chat-server: ${process.env.APP_VERSION}`) +console.log('DISABLE_CHAT:', process.env.DISABLE_CHAT ? 'YES' : 'NO') +console.log('DISABLE_COMPLETION:', process.env.DISABLE_COMPLETION ? 'YES' : 'NO') +console.log('DISABLE_OBJECT:', process.env.DISABLE_OBJECT ? 'YES' : 'NO') + console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY ? 'SET' : undefined) @@ -55,10 +59,10 @@ const maxOutputTokens = process.env.MAX_TOKENS : undefined console.log(`maxOutputTokens: ${maxOutputTokens}`) console.log( - `CHAT_INSTRUCTIONS: ${process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS}`, // NOSONAR + `INSTRUCTIONS_CHAT: ${process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT}`, // NOSONAR ) -console.log(`COMPLETION_INSTRUCTIONS: ${process.env.COMPLETION_INSTRUCTIONS}`) -console.log(`OBJECT_INSTRUCTIONS: ${process.env.OBJECT_INSTRUCTIONS}`) +console.log(`INSTRUCTIONS_COMPLETION: ${process.env.INSTRUCTIONS_COMPLETION}`) +console.log(`INSTRUCTIONS_OBJECT: ${process.env.INSTRUCTIONS_OBJECT}`) const model = getModel() console.log(`Loaded modelId: ${model.modelId}`) @@ -77,76 +81,82 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -app.post(['/', '/chat'], async (req: Request, res: Response) => { - // debug('req.headers:', req.headers) - // debug('authorization:', req.headers.authorization) - const { messages, system } = req.body - debug('system:', system?.length) - // debug('system:', system?.substring(0, 128)) - const modelMessages = await convertToModelMessages(messages) - debug('modelMessages:', modelMessages.length) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - const result = streamText({ - model: model, - messages: modelMessages, - system: system || process.env.INSTRUCTIONS || process.env.CHAT_INSTRUCTIONS, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ response: res, stream }) -}) - -app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: system || process.env.COMPLETION_INSTRUCTIONS, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ - response: res, - stream, - consumeSseStream: consumeStream, +if (!process.env.DISABLE_CHAT) { + app.post(['/', '/chat'], async (req: Request, res: Response) => { + // debug('req.headers:', req.headers) + // debug('authorization:', req.headers.authorization) + const { messages, system } = req.body + debug('system:', system?.length) + // debug('system:', system?.substring(0, 128)) + const modelMessages = await convertToModelMessages(messages) + debug('modelMessages:', modelMessages.length) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + const result = streamText({ + model: model, + messages: modelMessages, + system: system || process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, + }) + writer.merge(toUIMessageStream({ stream: result.stream })) + }, + }) + pipeUIMessageStreamToResponse({ response: res, stream }) }) -}) - -app.post('/object', async (req: Request, res: Response) => { - const { output, prompt, system } = req.body - debug('output:', output?.length) - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: system || process.env.OBJECT_INSTRUCTIONS, - maxOutputTokens, - providerOptions, - output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), - onError: onStreamError, - onEnd: onStreamEnd, +} + +if (!process.env.DISABLE_COMPLETION) { + app.post('/completion', async (req: Request, res: Response) => { + const { prompt, system } = req.body + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.INSTRUCTIONS_COMPLETION, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, + }) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + writer.merge(toUIMessageStream({ stream: result.stream })) + }, + }) + pipeUIMessageStreamToResponse({ + response: res, + stream, + consumeSseStream: consumeStream, + }) }) - pipeTextStreamToResponse({ - response: res, - stream: toTextStream({ stream: result.stream }), +} + +if (!process.env.DISABLE_OBJECT) { + app.post('/object', async (req: Request, res: Response) => { + const { output, prompt, system } = req.body + debug('output:', output?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: system || process.env.INSTRUCTIONS_OBJECT, + maxOutputTokens, + providerOptions, + output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), + onError: onStreamError, + onEnd: onStreamEnd, + }) + pipeTextStreamToResponse({ + response: res, + stream: toTextStream({ stream: result.stream }), + }) }) -}) +} function corsCallback( origin: string | undefined, From 888226e574d8b3363762c670ba00f1fe01603c4f Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:48:22 -0700 Subject: [PATCH 12/27] Update README.md --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7789a17..bd79901 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,7 @@ To send System Instructions from the client, add them to the body. import { useChat } from '@ai-sdk/vue' import { DefaultChatTransport } from 'ai' -const { messages, input, handleSubmit } = useChat({ +const { messages, sendMessage, status, stop } = useChat({ transport: new DefaultChatTransport({ api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, @@ -193,6 +193,8 @@ const { completion, complete, isLoading, stop } = useCompletion({ headers: { Authorization: 'Basic Abc123=' }, body: { system: 'You are a helpful assistant.' }, }) + +await complete('Explain how to setup cssnr/chat-server') ``` Reference: @@ -201,20 +203,21 @@ Reference: ```typescript import { useObject } from '@ai-sdk/vue' +import { z } from 'zod' +import { zodToJsonSchema } from 'zod-to-json-schema' + +const schema = z.object({ name: z.string(), age: z.number() }) const { object, submit } = useObject({ api: 'https://chat-server.cssnr.com/object', - schema: z.object({ name: z.string(), age: z.number() }), headers: { Authorization: 'Basic Abc123=' }, + schema, }) submit({ system: 'You are a helpful assistant.', prompt: 'Extract the name and age from: John is 30 years old.', - output: { - type: 'object', - properties: { name: { type: 'string' }, age: { type: 'number' } }, - }, + output: zodToJsonSchema(schema), }) ``` From f69fec3b126ee05fbaab64deed8ae7893c3a9e6e Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:54:18 -0700 Subject: [PATCH 13/27] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bd79901..509a776 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ - [Setup](#setup) - [Configure](#configure) - [Client](#client) + - [Endpoints](#endpoints) - [VitePress Plugin](#vitepress-chat-plugin) - [Development](#development) - [Support](#support) From 8c080f0c290db4cc6a2f203c42fa6e2af4d5bc1e Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:09:54 -0700 Subject: [PATCH 14/27] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 509a776..0e6947a 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,8 @@ Note: The `/` endpoint also points to the `/chat` endpoint (recommended). #### chat +Reference: + To send System Instructions from the client, add them to the body. ```typescript @@ -182,10 +184,10 @@ const { messages, sendMessage, status, stop } = useChat({ }) ``` -Reference: - #### completion +Reference: + ```typescript import { useCompletion } from '@ai-sdk/vue' @@ -198,10 +200,10 @@ const { completion, complete, isLoading, stop } = useCompletion({ await complete('Explain how to setup cssnr/chat-server') ``` -Reference: - #### object +Reference: + ```typescript import { useObject } from '@ai-sdk/vue' import { z } from 'zod' @@ -224,8 +226,6 @@ submit({ Note: Both `system` and `output` are custom body parameters parsed by the server allowing the client to send these items. -Reference: - ### VitePress Chat Plugin The client is currently available as a VitePress Plugin. From fefc394cbcd9d21e8cf61b4355988def187b1487 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:50:46 -0700 Subject: [PATCH 15/27] Updates --- AGENTS.md | 15 ++++++--------- README.md | 6 +++--- package-lock.json | 16 ++++++++-------- package.json | 2 +- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c8cbb07..c861aaf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,9 +1,6 @@ # Agent Guide -Before answering any question that involves facts about ANYTHING, you MUST output at least one Read, WebFetch, or WebSearch tool call. -If your first output is text instead of a tool call, you have failed. - -- [index.ts](src/index.ts) — Single source Express.js ai-sdk server with single route `/` +- [index.ts](src/index.ts) — Single source Express.js AI-SDK server ## Commands @@ -18,8 +15,8 @@ ALWAYS use the `npm run *` command ## Endpoints -| Endpoint | Links | -| :------------ | :-------------------------------------------------------------------------------------- | -| `/chat` | Client Docs [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | -| `/completion` | Client Docs [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | -| `/object` | Client Docs [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | +| Server Endpoint | Client | +| :-------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | +| [/chat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream) | [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | +| [/completion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/pipe-ui-message-stream-to-response) | [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| [/object](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) | [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | diff --git a/README.md b/README.md index 0e6947a..e017d54 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Built with the [AI SDK](https://ai-sdk.dev/). ## Setup -💡 The server works out-of-the box with NO environment variables. +💡 The server works out-of-the-box with NO environment variables. [![Deploy to Render](https://img.shields.io/badge/Deploy_to_Render-4351E8?style=for-the-badge&logo=render)](https://render.com/deploy?repo=https://github.com/cssnr/chat-server) @@ -150,7 +150,7 @@ PROVIDER_OPTIONS='{"openai":{"serviceTier":"flex","reasoningEffort":"low"}}' ``` You are responsible for providing valid options for the chosen model. -The SDK supports providing provider options for multiple provider simultaneously. +The SDK supports providing provider options for multiple providers simultaneously. The value is only checked for valid JSON at startup and will fail at runtime if it contains invalid options. ## Client @@ -197,7 +197,7 @@ const { completion, complete, isLoading, stop } = useCompletion({ body: { system: 'You are a helpful assistant.' }, }) -await complete('Explain how to setup cssnr/chat-server') +await complete('Explain how to set up cssnr/chat-server') ``` #### object diff --git a/package-lock.json b/package-lock.json index 2d7de4f..4f06126 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@ai-sdk/google": "^4.0.17", "@ai-sdk/openai": "^4.0.15", "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.29", + "ai": "^7.0.30", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -51,9 +51,9 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.21.tgz", - "integrity": "sha512-GafyeyHFDKJjdtbyhCQ0aC2FORdyE56IxK45EZ1JLO1iVdg8XqEU3bwnRzI0+5S1XHV7W2qQsxkZnGYPbsFiXA==", + "version": "4.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.22.tgz", + "integrity": "sha512-KdXJLa6O25fltJC/Lh8gnB1VaYMdX2mEKIalkohuItS+Ig523XxwK9e/Uuc66AvtrLIf7G51cDg3rDpP9mrxwQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", @@ -1222,12 +1222,12 @@ } }, "node_modules/ai": { - "version": "7.0.29", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.29.tgz", - "integrity": "sha512-q+A+skhl6SyjWliU6W7zNYgDUrEk5SbNrCc5vt4S9n6+n6e7jSorDmu51hlhjDOsS7VwzWGCgbWFvvT3iomtvg==", + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.30.tgz", + "integrity": "sha512-tm0gTAHSWBdDfW4P2mj+LlglGCUQTSA9ktyS2PsPgVhxNO9gYWTSnRAehiJ3h1lYsJBp1Zgbz3RH2lezJXagiw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.21", + "@ai-sdk/gateway": "4.0.22", "@ai-sdk/provider": "4.0.3", "@ai-sdk/provider-utils": "5.0.10" }, diff --git a/package.json b/package.json index 771f95e..6f6ee19 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@ai-sdk/google": "^4.0.17", "@ai-sdk/openai": "^4.0.15", "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.29", + "ai": "^7.0.30", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", From 22cd15444c95595e529162f7b5847304cc233393 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Thu, 16 Jul 2026 21:53:21 -0700 Subject: [PATCH 16/27] Remove star-history --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index e017d54..bf90d6e 100644 --- a/README.md +++ b/README.md @@ -295,11 +295,3 @@ and [additional](https://cssnr.com/) open source projects. [![Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/cssnr) For a full list of current projects visit: [https://cssnr.github.io/](https://cssnr.github.io/) - - - - - - Star History Chart - - From 538e5e91e20eb5147ad885b6256e3c8125aa3917 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 12:34:45 -0700 Subject: [PATCH 17/27] Add getBool --- .prettierrc.json | 2 +- README.md | 12 +++++---- package-lock.json | 66 +++++++++++++++++++++++------------------------ package.json | 12 ++++----- src/index.ts | 47 +++++++++++++++++---------------- 5 files changed, 72 insertions(+), 67 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 577fddf..d7dd7f6 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleQuote": true, - "printWidth": 90, + "printWidth": 94, "overrides": [ { "files": ["**/*.vue"], diff --git a/README.md b/README.md index bf90d6e..701595d 100644 --- a/README.md +++ b/README.md @@ -112,15 +112,17 @@ Environment Variables (can be placed in a `settings.env` file). | `INSTRUCTIONS_CHAT` | - | System Instructions for Chat | | `INSTRUCTIONS_COMPLETION` | - | System Instructions for Completion | | `INSTRUCTIONS_OBJECT` | - | System Instructions for Object | -| `DISABLE_CHAT` | - | Disable the `/chat` endpoint | -| `DISABLE_COMPLETION` | - | Disable the `/completion` endpoint | -| `DISABLE_OBJECT` | - | Disable the `/object` endpoint | -| `AI_SDK_LOG_WARNINGS` | - | Disable SDK Warnings | +| `DISABLE_CHAT`**¹** | - | Disable the `/chat` endpoint | +| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` endpoint | +| `DISABLE_OBJECT`**¹** | - | Disable the `/object` endpoint | +| `AI_SDK_LOG_WARNINGS`**¹** | - | Disable SDK Warnings | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | | `DEBUG` | - | Set to `app` for debug logs | -Note: The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). +> **¹** Boolean Variables. **True** values include: `['1', 't', 'true', 'y', 'yes', 'on']` + +The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). You must also set the API key for the `MODEL` you select. diff --git a/package-lock.json b/package-lock.json index 4f06126..987787f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.15", - "@ai-sdk/google": "^4.0.17", - "@ai-sdk/openai": "^4.0.15", - "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.30", + "@ai-sdk/anthropic": "^4.0.16", + "@ai-sdk/google": "^4.0.18", + "@ai-sdk/openai": "^4.0.16", + "@ai-sdk/openai-compatible": "^3.0.12", + "ai": "^7.0.31", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -35,13 +35,13 @@ } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.15.tgz", - "integrity": "sha512-6iVlzqZjqqoHTmgO1WU9id/33pYykIRasWJFAMf1+d+nhju6d7566VRFsOGZ2W2GzgNtzKs8DGsYN7X5/wOGuw==", + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.16.tgz", + "integrity": "sha512-vyH4D6Auih5H2xvVzzh2ep5pbdWiaV7JDC+jHUE7zZJ5Kyv0TteLav4DrOgHzRuyv8ptfUSqFF6Y8//f/Ec0fQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.22", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.22.tgz", - "integrity": "sha512-KdXJLa6O25fltJC/Lh8gnB1VaYMdX2mEKIalkohuItS+Ig523XxwK9e/Uuc66AvtrLIf7G51cDg3rDpP9mrxwQ==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.23.tgz", + "integrity": "sha512-f85diFdPMXYJpxCjOYZchMQkRH8h3r6lhK4Q2xmzJ7UA2OQ80L3W7tFu61742xGQK7zHWm5AhxYhNuc50H9SGQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10", + "@ai-sdk/provider-utils": "5.0.11", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.17.tgz", - "integrity": "sha512-90YMrWWIz7HVqrtthaP60fqGhsTT8OXHskxJ9sPmD/bFBHqdbOBrtS9oKBWjO0XBAY8eo2Rd7Ai8zGKCTvPn9Q==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.18.tgz", + "integrity": "sha512-NRbXRAasXLgFLiZDTsDUiTUuQtEUDVZoqruB5Px3geltTEqOzOo2eHN5WnDp0/OgxwnrNH4olV/TVetza0PzmQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.15.tgz", - "integrity": "sha512-JpTLQp5RUbRcs5nOyPEu5NRdxZLUnD/uCyT3qzy26D+iunCeL7KJV58ER9kwisAKnTjWravfNblaQNiWr20M9A==", + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.16.tgz", + "integrity": "sha512-Yh+PsXaf9NbN7oA3oKwOuyjTiHMPD75phf3SqGbDNNKQ3Yj3oTntp/WhO3nCHIPA6gr5/1lyridQokmOpPf9oQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.11.tgz", - "integrity": "sha512-PKLyt5PfjV2maWJPZpm3W29vCc+BbyqSPAcghXhl+iIxzFNjpGPa6UD/8J0b9l7w/lJCZKuhcef9q+Gp5WqUvw==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.12.tgz", + "integrity": "sha512-tN9BUb4jUGjqtbRPUsziLxASfogLNICcq/Qrwr55vpnzKvprV6Ukl8ynED2lZaNrAHU5U4zlGnyU/iuYrjQK6g==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" @@ -128,9 +128,9 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.10.tgz", - "integrity": "sha512-uPyec0+85dwxZYXtb8qe8gCjhjDfxP4LCDo/uRQS/iG+FIgYbHPRhr/ys281udG90bTaE18+5cxWraYaf8oHCw==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.11.tgz", + "integrity": "sha512-7/96wE+ZsKB35iS9ASyllrE4Ym/EolXEB7AkuJ5FI++fmS85BVTAs77890C+1Z2jwHfBKjBQSBmsliOsAh0iFQ==", "license": "Apache-2.0", "dependencies": { "@ai-sdk/provider": "4.0.3", @@ -1222,14 +1222,14 @@ } }, "node_modules/ai": { - "version": "7.0.30", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.30.tgz", - "integrity": "sha512-tm0gTAHSWBdDfW4P2mj+LlglGCUQTSA9ktyS2PsPgVhxNO9gYWTSnRAehiJ3h1lYsJBp1Zgbz3RH2lezJXagiw==", + "version": "7.0.31", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.31.tgz", + "integrity": "sha512-pJfwKXjF5kw0rKRTePwYo60EfWb8wfzJAgf3ojln/YkOsVVKttzZAJVcRPsg37Z3a06ZdKkxX+DSrMAFlPm5Mw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.22", + "@ai-sdk/gateway": "4.0.23", "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.10" + "@ai-sdk/provider-utils": "5.0.11" }, "engines": { "node": ">=22" diff --git a/package.json b/package.json index 6f6ee19..5bb4550 100644 --- a/package.json +++ b/package.json @@ -9,18 +9,18 @@ "dev": "nodemon --exec tsx src/index.ts", "lint": "npx eslint src", "prepare": "npm run build", - "prettier:": "npm run prettier:write", + "prettier": "npm run prettier:write", "prettier:check": "npx prettier --check .", "prettier:write": "npx prettier --write .", "start": "node dist/index.js", "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.15", - "@ai-sdk/google": "^4.0.17", - "@ai-sdk/openai": "^4.0.15", - "@ai-sdk/openai-compatible": "^3.0.11", - "ai": "^7.0.30", + "@ai-sdk/anthropic": "^4.0.16", + "@ai-sdk/google": "^4.0.18", + "@ai-sdk/openai": "^4.0.16", + "@ai-sdk/openai-compatible": "^3.0.12", + "ai": "^7.0.31", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", diff --git a/src/index.ts b/src/index.ts index 5d080d0..167f550 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,16 +23,16 @@ import { dotenv.config({ path: 'settings.env' }) -console.log(`DEBUG: ${process.env.DEBUG}`) +console.log('chat-server:', process.env.APP_VERSION) + +console.log('DEBUG:', process.env.DEBUG) createDebug.enable(process.env.DEBUG ?? '') const debug = createDebug('app') debug('debug enabled: app') -console.log(`chat-server: ${process.env.APP_VERSION}`) - -console.log('DISABLE_CHAT:', process.env.DISABLE_CHAT ? 'YES' : 'NO') -console.log('DISABLE_COMPLETION:', process.env.DISABLE_COMPLETION ? 'YES' : 'NO') -console.log('DISABLE_OBJECT:', process.env.DISABLE_OBJECT ? 'YES' : 'NO') +console.log('DISABLE_CHAT:', getBool(process.env.DISABLE_CHAT)) +console.log('DISABLE_COMPLETION:', getBool(process.env.DISABLE_COMPLETION)) +console.log('DISABLE_OBJECT:', getBool(process.env.DISABLE_OBJECT)) console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) @@ -41,12 +41,13 @@ console.log( 'GOOGLE_GENERATIVE_AI_API_KEY:', process.env.GOOGLE_GENERATIVE_AI_API_KEY ? 'SET' : undefined, ) + console.log('PROVIDER_API_KEY:', process.env.PROVIDER_API_KEY ? 'SET' : undefined) const baseURL = process.env.BASE_URL || 'https://opencode.ai/zen/v1' // NOSONAR console.log('BASE_URL:', baseURL) -if (process.env.AI_SDK_LOG_WARNINGS) globalThis.AI_SDK_LOG_WARNINGS = false -console.log('AI_SDK_LOG_WARNINGS:', process.env.AI_SDK_LOG_WARNINGS) +console.log('AI_SDK_LOG_WARNINGS:', getBool(process.env.AI_SDK_LOG_WARNINGS)) +if (!getBool(process.env.AI_SDK_LOG_WARNINGS)) globalThis.AI_SDK_LOG_WARNINGS = false const corsOrigins = process.env.CORS_ORIGINS?.split(/[, \n\r]+/) .map((s) => s.trim()) @@ -57,15 +58,14 @@ console.log('corsOrigins:', corsOrigins) const maxOutputTokens = process.env.MAX_TOKENS ? Number.parseInt(process.env.MAX_TOKENS) : undefined -console.log(`maxOutputTokens: ${maxOutputTokens}`) -console.log( - `INSTRUCTIONS_CHAT: ${process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT}`, // NOSONAR -) -console.log(`INSTRUCTIONS_COMPLETION: ${process.env.INSTRUCTIONS_COMPLETION}`) -console.log(`INSTRUCTIONS_OBJECT: ${process.env.INSTRUCTIONS_OBJECT}`) +console.log('maxOutputTokens:', maxOutputTokens) + +console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT) // NOSONAR +console.log('INSTRUCTIONS_COMPLETION:', process.env.INSTRUCTIONS_COMPLETION) +console.log('INSTRUCTIONS_OBJECT:', process.env.INSTRUCTIONS_OBJECT) const model = getModel() -console.log(`Loaded modelId: ${model.modelId}`) +console.log('Loaded modelId:', model.modelId) const providerOptions = getProviderOptions() console.log('providerOptions:', providerOptions) @@ -74,14 +74,12 @@ const app = express() const port = process.env.PORT || 3000 // NOSONAR app.use(express.json({ limit: '10mb' })) - app.use(cors({ origin: corsCallback })) - app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -if (!process.env.DISABLE_CHAT) { +if (!getBool(process.env.DISABLE_CHAT)) { app.post(['/', '/chat'], async (req: Request, res: Response) => { // debug('req.headers:', req.headers) // debug('authorization:', req.headers.authorization) @@ -108,7 +106,7 @@ if (!process.env.DISABLE_CHAT) { }) } -if (!process.env.DISABLE_COMPLETION) { +if (!getBool(process.env.DISABLE_COMPLETION)) { app.post('/completion', async (req: Request, res: Response) => { const { prompt, system } = req.body debug('prompt:', prompt?.length) @@ -135,7 +133,7 @@ if (!process.env.DISABLE_COMPLETION) { }) } -if (!process.env.DISABLE_OBJECT) { +if (!getBool(process.env.DISABLE_OBJECT)) { app.post('/object', async (req: Request, res: Response) => { const { output, prompt, system } = req.body debug('output:', output?.length) @@ -158,6 +156,11 @@ if (!process.env.DISABLE_OBJECT) { }) } +function getBool(value: string | undefined): boolean { + if (!value) return false + return ['1', 't', 'true', 'y', 'yes', 'on'].includes(value.trim().toLowerCase()) +} + function corsCallback( origin: string | undefined, callback: (err: Error | null, origin?: boolean) => void, @@ -196,8 +199,8 @@ function getProviderOptions() { if (!process.env.PROVIDER_OPTIONS) return try { return JSON.parse(process.env.PROVIDER_OPTIONS) - } catch { - console.error('parsing PROVIDER_OPTIONS as JSON') + } catch (e) { + console.error('error parsing PROVIDER_OPTIONS as JSON:', e) } } From 21ae90fee940179487c8d8fe4deb6718aced4a71 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 14:10:23 -0700 Subject: [PATCH 18/27] Update README.md --- README.md | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 701595d..5301149 100644 --- a/README.md +++ b/README.md @@ -103,27 +103,25 @@ For a Portainer Deploy workflow see the [.github/workflows/deploy.yaml](https:// Environment Variables (can be placed in a `settings.env` file). -| Variable | Default | Description | -| :------------------------------------ | :--------------------------- | :---------------------------------- | -| `MODEL` | `big-pickle` | Model to Use | -| `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | -| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | -| `MAX_TOKENS` | - | Max Output Tokens | -| `INSTRUCTIONS_CHAT` | - | System Instructions for Chat | -| `INSTRUCTIONS_COMPLETION` | - | System Instructions for Completion | -| `INSTRUCTIONS_OBJECT` | - | System Instructions for Object | -| `DISABLE_CHAT`**¹** | - | Disable the `/chat` endpoint | -| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` endpoint | -| `DISABLE_OBJECT`**¹** | - | Disable the `/object` endpoint | -| `AI_SDK_LOG_WARNINGS`**¹** | - | Disable SDK Warnings | -| `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | -| `PORT` | `3000` | Server Port | -| `DEBUG` | - | Set to `app` for debug logs | +| Variable | Default | Description | +| :--------------------------------------- | :--------------------------- | :---------------------------------- | +| `MODEL` | `big-pickle` | Model to Use | +| `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | +| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | +| `MAX_TOKENS` | - | Max Output Tokens | +| [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | +| [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | +| [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | +| `DISABLE_CHAT`**¹** | - | Disable the `/chat` Endpoint | +| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` Endpoint | +| `DISABLE_OBJECT`**¹** | - | Disable the `/object` Endpoint | +| `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | +| `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | +| `PORT` | `3000` | Server Port | +| `DEBUG` | - | Set to `app` for Debug Logging | > **¹** Boolean Variables. **True** values include: `['1', 't', 'true', 'y', 'yes', 'on']` -The `INSTRUCTIONS` variable also points to the `INSTRUCTIONS_CHAT` variable (recommended). - You must also set the API key for the `MODEL` you select. | Variable | Description | @@ -135,6 +133,13 @@ You must also set the API key for the `MODEL` you select. The `PROVIDER_API_KEY` is optional for free-tier models like `big-pickle`. +#### INSTRUCTIONS + +There are mechanisms to override the instructions per-call for all clients on all endpoints. +These are used as fallback when those instructions are not sent for configurations where this is desired. + +The `INSTRUCTIONS` variable (legacy) also points to the `INSTRUCTIONS_CHAT` variable (recommended). + #### PROVIDER_OPTIONS Provider Options: @@ -165,14 +170,12 @@ The value is only checked for valid JSON at startup and will fail at runtime if | `/completion` | `POST` | Use with [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | | `/object` | `POST` | Use with [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | -Note: The `/` endpoint also points to the `/chat` endpoint (recommended). +Note: The `/` endpoint (legacy) also points to the `/chat` endpoint (recommended). #### chat Reference: -To send System Instructions from the client, add them to the body. - ```typescript import { useChat } from '@ai-sdk/vue' import { DefaultChatTransport } from 'ai' @@ -186,6 +189,8 @@ const { messages, sendMessage, status, stop } = useChat({ }) ``` +To send System Instructions from the client, add them to the body. + #### completion Reference: @@ -202,6 +207,8 @@ const { completion, complete, isLoading, stop } = useCompletion({ await complete('Explain how to set up cssnr/chat-server') ``` +To send System Instructions from the client, add them to the body. + #### object Reference: @@ -226,7 +233,9 @@ submit({ }) ``` -Note: Both `system` and `output` are custom body parameters parsed by the server allowing the client to send these items. +To send System Instructions and Output Schema from the client, add them to the body. + +Note: Both `system` and `output` are custom body parameters parsed by the server. ### VitePress Chat Plugin From 64a9ae0595588cf385e679b83e792457dc1299aa Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:20:30 -0700 Subject: [PATCH 19/27] Add DISABLE_CLIENT_INSTRUCTIONS --- README.md | 5 ++++- src/index.ts | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5301149..09071aa 100644 --- a/README.md +++ b/README.md @@ -107,14 +107,15 @@ Environment Variables (can be placed in a `settings.env` file). | :--------------------------------------- | :--------------------------- | :---------------------------------- | | `MODEL` | `big-pickle` | Model to Use | | `BASE_URL` | `https://opencode.ai/zen/v1` | OpenAI Compatible Provider Base URL | -| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | `MAX_TOKENS` | - | Max Output Tokens | +| [PROVIDER_OPTIONS](#PROVIDER_OPTIONS) | - | Provider Options JSON String | | [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | | [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | | [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | | `DISABLE_CHAT`**¹** | - | Disable the `/chat` Endpoint | | `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` Endpoint | | `DISABLE_OBJECT`**¹** | - | Disable the `/object` Endpoint | +| `DISABLE_CLIENT_INSTRUCTIONS`**¹** | - | Ignore Client System Instructions | | `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | | `PORT` | `3000` | Server Port | @@ -140,6 +141,8 @@ These are used as fallback when those instructions are not sent for configuratio The `INSTRUCTIONS` variable (legacy) also points to the `INSTRUCTIONS_CHAT` variable (recommended). +To disable the clients ability to send custom instructions set `DISABLE_CLIENT_INSTRUCTIONS=true` + #### PROVIDER_OPTIONS Provider Options: diff --git a/src/index.ts b/src/index.ts index 167f550..0ddbfcb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,7 +60,11 @@ const maxOutputTokens = process.env.MAX_TOKENS : undefined console.log('maxOutputTokens:', maxOutputTokens) -console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT) // NOSONAR +const disableInstructions = getBool(process.env.DISABLE_CLIENT_INSTRUCTIONS) +console.log('disableInstructions:', disableInstructions) + +process.env.INSTRUCTIONS_CHAT = process.env.INSTRUCTIONS_CHAT || process.env.INSTRUCTIONS // NOSONAR +console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS_CHAT) console.log('INSTRUCTIONS_COMPLETION:', process.env.INSTRUCTIONS_COMPLETION) console.log('INSTRUCTIONS_OBJECT:', process.env.INSTRUCTIONS_OBJECT) @@ -93,7 +97,7 @@ if (!getBool(process.env.DISABLE_CHAT)) { const result = streamText({ model: model, messages: modelMessages, - system: system || process.env.INSTRUCTIONS || process.env.INSTRUCTIONS_CHAT, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, maxOutputTokens, providerOptions, onError: onStreamError, @@ -114,7 +118,7 @@ if (!getBool(process.env.DISABLE_COMPLETION)) { const result = streamText({ model: model, prompt, - system: system || process.env.INSTRUCTIONS_COMPLETION, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, maxOutputTokens, providerOptions, onError: onStreamError, @@ -142,7 +146,7 @@ if (!getBool(process.env.DISABLE_OBJECT)) { const result = streamText({ model: model, prompt, - system: system || process.env.INSTRUCTIONS_OBJECT, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), From 295fc7c7de56bb67cfd76c93b2d34c99e1e4fd41 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:13:13 -0700 Subject: [PATCH 20/27] Remove Disable Options --- README.md | 3 -- src/index.ts | 144 ++++++++++++++++++++++++--------------------------- 2 files changed, 67 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 09071aa..1273187 100644 --- a/README.md +++ b/README.md @@ -112,9 +112,6 @@ Environment Variables (can be placed in a `settings.env` file). | [INSTRUCTIONS_CHAT](#INSTRUCTIONS) | - | System Instructions for Chat | | [INSTRUCTIONS_COMPLETION](#INSTRUCTIONS) | - | System Instructions for Completion | | [INSTRUCTIONS_OBJECT](#INSTRUCTIONS) | - | System Instructions for Object | -| `DISABLE_CHAT`**¹** | - | Disable the `/chat` Endpoint | -| `DISABLE_COMPLETION`**¹** | - | Disable the `/completion` Endpoint | -| `DISABLE_OBJECT`**¹** | - | Disable the `/object` Endpoint | | `DISABLE_CLIENT_INSTRUCTIONS`**¹** | - | Ignore Client System Instructions | | `AI_SDK_LOG_WARNINGS`**¹** | - | Enable SDK Warnings Logging | | `CORS_ORIGINS` | - | Allowed CORS Origins (supports \*) | diff --git a/src/index.ts b/src/index.ts index 0ddbfcb..39f328b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,10 +30,6 @@ createDebug.enable(process.env.DEBUG ?? '') const debug = createDebug('app') debug('debug enabled: app') -console.log('DISABLE_CHAT:', getBool(process.env.DISABLE_CHAT)) -console.log('DISABLE_COMPLETION:', getBool(process.env.DISABLE_COMPLETION)) -console.log('DISABLE_OBJECT:', getBool(process.env.DISABLE_OBJECT)) - console.log('MODEL:', process.env.MODEL) console.log('ANTHROPIC_API_KEY:', process.env.ANTHROPIC_API_KEY ? 'SET' : undefined) console.log('OPENAI_API_KEY:', process.env.OPENAI_API_KEY ? 'SET' : undefined) @@ -83,82 +79,76 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) // app.get('/app-health-check', (_req, res) => res.sendStatus(200)) -if (!getBool(process.env.DISABLE_CHAT)) { - app.post(['/', '/chat'], async (req: Request, res: Response) => { - // debug('req.headers:', req.headers) - // debug('authorization:', req.headers.authorization) - const { messages, system } = req.body - debug('system:', system?.length) - // debug('system:', system?.substring(0, 128)) - const modelMessages = await convertToModelMessages(messages) - debug('modelMessages:', modelMessages.length) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - const result = streamText({ - model: model, - messages: modelMessages, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ response: res, stream }) +app.post(['/', '/chat'], async (req: Request, res: Response) => { + // debug('req.headers:', req.headers) + // debug('authorization:', req.headers.authorization) + const { messages, system } = req.body + debug('system:', system?.length) + // debug('system:', system?.substring(0, 128)) + const modelMessages = await convertToModelMessages(messages) + debug('modelMessages:', modelMessages.length) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + const result = streamText({ + model: model, + messages: modelMessages, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, + }) + writer.merge(toUIMessageStream({ stream: result.stream })) + }, }) -} - -if (!getBool(process.env.DISABLE_COMPLETION)) { - app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, - maxOutputTokens, - providerOptions, - onError: onStreamError, - onEnd: onStreamEnd, - }) - const stream = createUIMessageStream({ - execute: ({ writer }) => { - writer.merge(toUIMessageStream({ stream: result.stream })) - }, - }) - pipeUIMessageStreamToResponse({ - response: res, - stream, - consumeSseStream: consumeStream, - }) + pipeUIMessageStreamToResponse({ response: res, stream }) +}) + +app.post('/completion', async (req: Request, res: Response) => { + const { prompt, system } = req.body + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, + maxOutputTokens, + providerOptions, + onError: onStreamError, + onEnd: onStreamEnd, }) -} - -if (!getBool(process.env.DISABLE_OBJECT)) { - app.post('/object', async (req: Request, res: Response) => { - const { output, prompt, system } = req.body - debug('output:', output?.length) - debug('prompt:', prompt?.length) - debug('system:', system?.length) - const result = streamText({ - model: model, - prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, - maxOutputTokens, - providerOptions, - output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), - onError: onStreamError, - onEnd: onStreamEnd, - }) - pipeTextStreamToResponse({ - response: res, - stream: toTextStream({ stream: result.stream }), - }) + const stream = createUIMessageStream({ + execute: ({ writer }) => { + writer.merge(toUIMessageStream({ stream: result.stream })) + }, }) -} + pipeUIMessageStreamToResponse({ + response: res, + stream, + consumeSseStream: consumeStream, + }) +}) + +app.post('/object', async (req: Request, res: Response) => { + const { output, prompt, system } = req.body + debug('output:', output?.length) + debug('prompt:', prompt?.length) + debug('system:', system?.length) + const result = streamText({ + model: model, + prompt, + system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, + maxOutputTokens, + providerOptions, + output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), + onError: onStreamError, + onEnd: onStreamEnd, + }) + pipeTextStreamToResponse({ + response: res, + stream: toTextStream({ stream: result.stream }), + }) +}) function getBool(value: string | undefined): boolean { if (!value) return false From 512527c954d6de4ff6d952e67f79d1b5ea4009d1 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 21:40:41 -0700 Subject: [PATCH 21/27] Update system to instructions --- src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 39f328b..ea26d82 100644 --- a/src/index.ts +++ b/src/index.ts @@ -92,7 +92,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, + instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, maxOutputTokens, providerOptions, onError: onStreamError, @@ -111,7 +111,7 @@ app.post('/completion', async (req: Request, res: Response) => { const result = streamText({ model: model, prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, + instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, maxOutputTokens, providerOptions, onError: onStreamError, @@ -137,7 +137,7 @@ app.post('/object', async (req: Request, res: Response) => { const result = streamText({ model: model, prompt, - system: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, + instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), @@ -198,11 +198,11 @@ function getProviderOptions() { } } -function onStreamError(error: unknown) { +async function onStreamError({ error }: { error: unknown }) { console.error('error:', error) } -function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { +async function onStreamEnd({ finalStep, finishReason, text, usage }: GenerateTextEndEvent) { debug('reasoning:', finalStep.reasoningText) debug('response:', text) debug('usage:', usage) From e4c38ab11b53d6ef02c33c39f8e288257cc8f00d Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Fri, 17 Jul 2026 23:14:15 -0700 Subject: [PATCH 22/27] Update Client system to instructions --- .prettierrc.json | 2 +- README.md | 8 ++++---- src/index.ts | 23 +++++++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index d7dd7f6..a1ceab5 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,7 +2,7 @@ "$schema": "https://json.schemastore.org/prettierrc", "semi": false, "singleQuote": true, - "printWidth": 94, + "printWidth": 96, "overrides": [ { "files": ["**/*.vue"], diff --git a/README.md b/README.md index 1273187..ffcdc48 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ const { messages, sendMessage, status, stop } = useChat({ transport: new DefaultChatTransport({ api: 'https://chat-server.cssnr.com/chat', headers: { Authorization: 'Basic Abc123=' }, - body: { system: 'You are a helpful assistant.' }, + body: { instructions: 'You are a helpful assistant.' }, }), }) ``` @@ -201,7 +201,7 @@ import { useCompletion } from '@ai-sdk/vue' const { completion, complete, isLoading, stop } = useCompletion({ api: 'https://chat-server.cssnr.com/completion', headers: { Authorization: 'Basic Abc123=' }, - body: { system: 'You are a helpful assistant.' }, + body: { instructions: 'You are a helpful assistant.' }, }) await complete('Explain how to set up cssnr/chat-server') @@ -227,7 +227,7 @@ const { object, submit } = useObject({ }) submit({ - system: 'You are a helpful assistant.', + instructions: 'You are a helpful assistant.', prompt: 'Extract the name and age from: John is 30 years old.', output: zodToJsonSchema(schema), }) @@ -235,7 +235,7 @@ submit({ To send System Instructions and Output Schema from the client, add them to the body. -Note: Both `system` and `output` are custom body parameters parsed by the server. +Note: Both `instructions` and `output` are custom body parameters parsed by the server. ### VitePress Chat Plugin diff --git a/src/index.ts b/src/index.ts index ea26d82..3361912 100644 --- a/src/index.ts +++ b/src/index.ts @@ -82,9 +82,9 @@ app.listen(port, () => console.log(`Listening on PORT: ${port}`)) app.post(['/', '/chat'], async (req: Request, res: Response) => { // debug('req.headers:', req.headers) // debug('authorization:', req.headers.authorization) - const { messages, system } = req.body - debug('system:', system?.length) - // debug('system:', system?.substring(0, 128)) + const { instructions, messages, system } = req.body + debug('instructions:', (instructions || system)?.length) + // debug('instructions:', (instructions || system)?.substring(0, 128)) const modelMessages = await convertToModelMessages(messages) debug('modelMessages:', modelMessages.length) const stream = createUIMessageStream({ @@ -92,7 +92,8 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_CHAT, + instructions: + (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_CHAT, maxOutputTokens, providerOptions, onError: onStreamError, @@ -105,13 +106,14 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { }) app.post('/completion', async (req: Request, res: Response) => { - const { prompt, system } = req.body + const { instructions, prompt, system } = req.body + debug('instructions:', (instructions || system)?.length) debug('prompt:', prompt?.length) - debug('system:', system?.length) const result = streamText({ model: model, prompt, - instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_COMPLETION, + instructions: + (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_COMPLETION, maxOutputTokens, providerOptions, onError: onStreamError, @@ -130,14 +132,15 @@ app.post('/completion', async (req: Request, res: Response) => { }) app.post('/object', async (req: Request, res: Response) => { - const { output, prompt, system } = req.body + const { instructions, output, prompt, system } = req.body + debug('instructions:', (instructions || system)?.length) debug('output:', output?.length) debug('prompt:', prompt?.length) - debug('system:', system?.length) const result = streamText({ model: model, prompt, - instructions: (!disableInstructions && system) || process.env.INSTRUCTIONS_OBJECT, + instructions: + (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_OBJECT, maxOutputTokens, providerOptions, output: output ? Output.object({ schema: jsonSchema(output) }) : Output.json(), From e6775d190f4a9988e4efe5f36cf047c495ff29ee Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sat, 18 Jul 2026 08:22:25 -0700 Subject: [PATCH 23/27] Update AGENTS.md --- AGENTS.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c861aaf..6227489 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,10 @@ # Agent Guide - [index.ts](src/index.ts) — Single source Express.js AI-SDK server +- [README.md](README.md) — Server documentation + +- [AI SDK](https://ai-sdk.dev/docs/reference) +- [Express.js](https://expressjs.com/en/5x/api/) ## Commands @@ -13,10 +17,14 @@ ALWAYS use the `npm run *` command | `npm run tsc` | TypeScript Check Only `tsc --noEmit` | | `npm run prettier` | ALWAYS RUN AFTER EDITING FILES | -## Endpoints +## Endpoints and Documentation + +| Server Endpoint | Client | +| :-------------- | :-------------------------------------------------------------------------- | +| `/chat` | [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | +| `/completion` | [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | +| `/object` | [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | -| Server Endpoint | Client | -| :-------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | -| [/chat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream) | [useChat](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) | -| [/completion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/pipe-ui-message-stream-to-response) | [useCompletion](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-completion) | -| [/object](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) | [useObject](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-object) | +- [createUIMessageStream](https://ai-sdk.dev/docs/reference/ai-sdk-ui/create-ui-message-stream) +- [pipeUIMessageStreamToResponse](https://ai-sdk.dev/docs/reference/ai-sdk-ui/pipe-ui-message-stream-to-response) +- [streamText](https://ai-sdk.dev/docs/reference/ai-sdk-core/stream-text) From ff7ce9b7ee0b94d91f54d904cf5be52228d5c382 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:48:53 -0700 Subject: [PATCH 24/27] Update Dependencies/Workflows --- .github/workflows/labeler.yaml | 2 +- .github/workflows/lint.yaml | 2 +- package-lock.json | 314 +++++++++++++++++---------------- package.json | 20 +-- 4 files changed, 176 insertions(+), 162 deletions(-) diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 922b063..c37a7bc 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -46,7 +46,7 @@ jobs: file: .configs/labels/labels.yaml - name: "Labeler" - uses: actions/labeler@v6 + uses: actions/labeler@v7 with: sync-labels: true configuration-path: .configs/labels/labeler.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 5ea343e..f12c0eb 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -66,7 +66,7 @@ jobs: - name: "hadolint" if: ${{ !cancelled() }} - uses: hadolint/hadolint-action@v3.3.0 + uses: hadolint/hadolint-action@v3.4.0 with: dockerfile: Dockerfile ignore: "DL3018" diff --git a/package-lock.json b/package-lock.json index 987787f..583cca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.16", - "@ai-sdk/google": "^4.0.18", - "@ai-sdk/openai": "^4.0.16", - "@ai-sdk/openai-compatible": "^3.0.12", - "ai": "^7.0.31", + "@ai-sdk/anthropic": "^4.0.27", + "@ai-sdk/google": "^4.0.31", + "@ai-sdk/openai": "^4.0.27", + "@ai-sdk/openai-compatible": "^3.0.20", + "ai": "^7.0.48", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -24,24 +24,24 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.1", + "@types/node": "^26.1.2", "@types/picomatch": "^4.0.3", - "eslint": "^10.7.0", + "eslint": "^10.8.0", "nodemon": "^3.1.14", - "prettier": "^3.9.5", - "tsx": "^4.23.1", + "prettier": "^3.9.6", + "tsx": "^4.23.4", "typescript": "^6.0.3", - "typescript-eslint": "^8.64.0" + "typescript-eslint": "^8.65.0" } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.16.tgz", - "integrity": "sha512-vyH4D6Auih5H2xvVzzh2ep5pbdWiaV7JDC+jHUE7zZJ5Kyv0TteLav4DrOgHzRuyv8ptfUSqFF6Y8//f/Ec0fQ==", + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.27.tgz", + "integrity": "sha512-ecrPWkYf+sHDLHErHAU/yjJfNevUzT0i6TuoQLb58oadnquSHcLQ5NMLLWjbr6d1rxYdzI7ftulIJ03VH9yM3w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.23.tgz", - "integrity": "sha512-f85diFdPMXYJpxCjOYZchMQkRH8h3r6lhK4Q2xmzJ7UA2OQ80L3W7tFu61742xGQK7zHWm5AhxYhNuc50H9SGQ==", + "version": "4.0.37", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.37.tgz", + "integrity": "sha512-YBCTsSlX0ETVsjo0ihfBOeJ3p3y1wXKXDzF9Ygp9dscUY06dzOGmyWJSGsKg+khKVArzBWUW4UCSAKms20ZDmg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11", + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.18.tgz", - "integrity": "sha512-NRbXRAasXLgFLiZDTsDUiTUuQtEUDVZoqruB5Px3geltTEqOzOo2eHN5WnDp0/OgxwnrNH4olV/TVetza0PzmQ==", + "version": "4.0.31", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.31.tgz", + "integrity": "sha512-AFyO4MuHryrzr/ZEMHm/dEXwqABqoi4yIa9cfZxfI4VYM+dyPeZIXU4/qjvxiD6vzbitQhYf1jutQL/d71bymw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.16.tgz", - "integrity": "sha512-Yh+PsXaf9NbN7oA3oKwOuyjTiHMPD75phf3SqGbDNNKQ3Yj3oTntp/WhO3nCHIPA6gr5/1lyridQokmOpPf9oQ==", + "version": "4.0.27", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.27.tgz", + "integrity": "sha512-6MsWnbNG0ZjZgzYimNNjnTAlOFXn+jdLMFB47dDVUsRNjxdUoTSduCQPZZeTKoUl0ZNwCjsJ31of49l/t1r9MA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.12.tgz", - "integrity": "sha512-tN9BUb4jUGjqtbRPUsziLxASfogLNICcq/Qrwr55vpnzKvprV6Ukl8ynED2lZaNrAHU5U4zlGnyU/iuYrjQK6g==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.20.tgz", + "integrity": "sha512-RsPg+HilsKc/d+jy4zItG7v7RJQm+caUu+iU0a5bx3eyyX5tkayMGDRklXMMMAPa4l34luzQwT3t4cmeHfdiWw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -116,9 +116,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.3.tgz", - "integrity": "sha512-e0CpNWJUY7OxAFAnCZkw+ri9QOHWwTs1tXP42782KFGCU07qt8NiXCrCVowyCB5dP2r5/Uls+g2oPd8kOJn9dw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.4.tgz", + "integrity": "sha512-tbHKNLirllUNF3ZlkCsXnwab2ZV1Sl4b1H/Cp9ruCce15IBmskE8Gwkk0yo9xDWY+jho2of7lVXtwSsyrq7cwQ==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -128,15 +128,16 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.11.tgz", - "integrity": "sha512-7/96wE+ZsKB35iS9ASyllrE4Ym/EolXEB7AkuJ5FI++fmS85BVTAs77890C+1Z2jwHfBKjBQSBmsliOsAh0iFQ==", + "version": "5.0.18", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.18.tgz", + "integrity": "sha512-UBNCrkxS5llgN2/RXLBRkjCFTIuL6YB1Goq5c+yRecnznhtX4XPjTwLHz2hrsTltTVZ0rqVegtnwFXdPBkjDHA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.3", + "@ai-sdk/provider": "4.0.4", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", - "eventsource-parser": "^3.0.8" + "eventsource-parser": "^3.0.8", + "undici": "^7.28.0" }, "engines": { "node": ">=22" @@ -588,9 +589,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -645,9 +646,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -855,9 +856,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", - "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", + "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", "dev": true, "license": "MIT", "dependencies": { @@ -889,9 +890,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", - "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", "dev": true, "license": "MIT", "dependencies": { @@ -941,17 +942,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", - "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.64.0", - "@typescript-eslint/type-utils": "8.64.0", - "@typescript-eslint/utils": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -964,7 +965,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.64.0", + "@typescript-eslint/parser": "^8.65.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -980,16 +981,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", - "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.64.0", - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" }, "engines": { @@ -1005,14 +1006,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", - "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.64.0", - "@typescript-eslint/types": "^8.64.0", + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" }, "engines": { @@ -1027,14 +1028,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", - "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1045,9 +1046,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", - "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", "dev": true, "license": "MIT", "engines": { @@ -1062,15 +1063,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", - "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0", - "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1087,9 +1088,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", - "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -1101,16 +1102,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", - "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.64.0", - "@typescript-eslint/tsconfig-utils": "8.64.0", - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/visitor-keys": "8.64.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1129,16 +1130,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", - "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.64.0", - "@typescript-eslint/types": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1153,13 +1154,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", - "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1199,9 +1200,9 @@ } }, "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -1222,14 +1223,14 @@ } }, "node_modules/ai": { - "version": "7.0.31", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.31.tgz", - "integrity": "sha512-pJfwKXjF5kw0rKRTePwYo60EfWb8wfzJAgf3ojln/YkOsVVKttzZAJVcRPsg37Z3a06ZdKkxX+DSrMAFlPm5Mw==", + "version": "7.0.48", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.48.tgz", + "integrity": "sha512-QmqshWFEDdkFFqSgdJ4cogaoDIYaedqbv73q/NXEYNkSIocJCzXnGFVyM9lrtAGTSBfm+5hq3/292ooXJ1jDSw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.23", - "@ai-sdk/provider": "4.0.3", - "@ai-sdk/provider-utils": "5.0.11" + "@ai-sdk/gateway": "4.0.37", + "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider-utils": "5.0.18" }, "engines": { "node": ">=22" @@ -1343,16 +1344,16 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/braces": { @@ -1682,9 +1683,9 @@ } }, "node_modules/eslint": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", - "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -1694,7 +1695,7 @@ "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", + "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", @@ -1718,7 +1719,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2015,9 +2016,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -2373,12 +2374,16 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/merge-descriptors": { @@ -2419,13 +2424,13 @@ } }, "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -2649,9 +2654,9 @@ } }, "node_modules/prettier": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.5.tgz", - "integrity": "sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -3043,9 +3048,9 @@ } }, "node_modules/tsx": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", - "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", + "version": "4.23.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.4.tgz", + "integrity": "sha512-ZiUQ8oT/KzN51mJUWPqARYqwFLFJZtGZipRkw1ynHMr9vy3eU77m5yfF3Gzm6meEg/beW+lUu3fHYgskTN2oVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3120,16 +3125,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.64.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", - "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.64.0", - "@typescript-eslint/parser": "8.64.0", - "@typescript-eslint/typescript-estree": "8.64.0", - "@typescript-eslint/utils": "8.64.0" + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3150,6 +3155,15 @@ "dev": true, "license": "MIT" }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", diff --git a/package.json b/package.json index 5bb4550..277f8f9 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,11 @@ "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.16", - "@ai-sdk/google": "^4.0.18", - "@ai-sdk/openai": "^4.0.16", - "@ai-sdk/openai-compatible": "^3.0.12", - "ai": "^7.0.31", + "@ai-sdk/anthropic": "^4.0.27", + "@ai-sdk/google": "^4.0.31", + "@ai-sdk/openai": "^4.0.27", + "@ai-sdk/openai-compatible": "^3.0.20", + "ai": "^7.0.48", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -32,13 +32,13 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.1", + "@types/node": "^26.1.2", "@types/picomatch": "^4.0.3", - "eslint": "^10.7.0", + "eslint": "^10.8.0", "nodemon": "^3.1.14", - "prettier": "^3.9.5", - "tsx": "^4.23.1", + "prettier": "^3.9.6", + "tsx": "^4.23.4", "typescript": "^6.0.3", - "typescript-eslint": "^8.64.0" + "typescript-eslint": "^8.65.0" } } From ab1755f3b4decd3c3a1e53f02caf89fdc19660f4 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:55:04 -0700 Subject: [PATCH 25/27] Fix hadolint DL3066 --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9c939f4..84a6176 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ ARG VERSION="Dockerfile" ENV APP_VERSION="${VERSION}" LABEL org.opencontainers.image.version="${VERSION}" -USER node +#USER node +USER 1000 CMD ["npm", "start"] From 99029ab53f28f0d8c48338ee98ccd54549f71ff9 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 16 Aug 2026 11:56:14 -0700 Subject: [PATCH 26/27] Add Default Headers for Default Zen Provider --- .ncurc.yaml | 2 + package-lock.json | 466 ++++++++++++++++++++++---------------------- package.json | 19 +- src/index.ts | 35 +++- test/object.test.ts | 108 ++++++++++ 5 files changed, 380 insertions(+), 250 deletions(-) create mode 100644 .ncurc.yaml create mode 100644 test/object.test.ts diff --git a/.ncurc.yaml b/.ncurc.yaml new file mode 100644 index 0000000..dd1af0f --- /dev/null +++ b/.ncurc.yaml @@ -0,0 +1,2 @@ +reject: + - typescript diff --git a/package-lock.json b/package-lock.json index 583cca6..52fedc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,11 @@ "name": "chat-server", "version": "0.0.0", "dependencies": { - "@ai-sdk/anthropic": "^4.0.27", - "@ai-sdk/google": "^4.0.31", - "@ai-sdk/openai": "^4.0.27", - "@ai-sdk/openai-compatible": "^3.0.20", - "ai": "^7.0.48", + "@ai-sdk/anthropic": "^4.0.39", + "@ai-sdk/google": "^4.0.44", + "@ai-sdk/openai": "^4.0.42", + "@ai-sdk/openai-compatible": "^3.0.30", + "ai": "^7.0.66", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -24,24 +24,24 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.2", + "@types/node": "^26.2.0", "@types/picomatch": "^4.0.3", - "eslint": "^10.8.0", + "eslint": "^10.8.1", "nodemon": "^3.1.14", "prettier": "^3.9.6", - "tsx": "^4.23.4", + "tsx": "^4.23.12", "typescript": "^6.0.3", - "typescript-eslint": "^8.65.0" + "typescript-eslint": "^8.67.0" } }, "node_modules/@ai-sdk/anthropic": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.27.tgz", - "integrity": "sha512-ecrPWkYf+sHDLHErHAU/yjJfNevUzT0i6TuoQLb58oadnquSHcLQ5NMLLWjbr6d1rxYdzI7ftulIJ03VH9yM3w==", + "version": "4.0.39", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-4.0.39.tgz", + "integrity": "sha512-JAMGtYeEuaBzqbsPO4fkho6vQyNoVhsHASM4o59wmJRU6Vh7prjOp490Kmc7YQTY+ioU1/xYzXvWOtxZBup0Xw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -51,13 +51,13 @@ } }, "node_modules/@ai-sdk/gateway": { - "version": "4.0.37", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.37.tgz", - "integrity": "sha512-YBCTsSlX0ETVsjo0ihfBOeJ3p3y1wXKXDzF9Ygp9dscUY06dzOGmyWJSGsKg+khKVArzBWUW4UCSAKms20ZDmg==", + "version": "4.0.52", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.52.tgz", + "integrity": "sha512-SXUM8jzzuTUJRq+EOgPd5to6DSx0EKslVn+IVZHbUEX6k/3vCPNrvjckbK26HnNxHU/STxm+zTSJteqrO+7Z0w==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18", + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27", "@vercel/oidc": "3.2.0" }, "engines": { @@ -68,13 +68,13 @@ } }, "node_modules/@ai-sdk/google": { - "version": "4.0.31", - "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.31.tgz", - "integrity": "sha512-AFyO4MuHryrzr/ZEMHm/dEXwqABqoi4yIa9cfZxfI4VYM+dyPeZIXU4/qjvxiD6vzbitQhYf1jutQL/d71bymw==", + "version": "4.0.44", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-4.0.44.tgz", + "integrity": "sha512-bmRTDg06jQD+eX8nf214pET9+Oe8O1+lUIRGbWsGXj9IN2UJkpl1O1x7cvtiboyTtKSLvSRdVtItUfSl8sQ2GA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -84,13 +84,13 @@ } }, "node_modules/@ai-sdk/openai": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.27.tgz", - "integrity": "sha512-6MsWnbNG0ZjZgzYimNNjnTAlOFXn+jdLMFB47dDVUsRNjxdUoTSduCQPZZeTKoUl0ZNwCjsJ31of49l/t1r9MA==", + "version": "4.0.42", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-4.0.42.tgz", + "integrity": "sha512-ZxDca6jJalYuXrIGVrw6dnkpz1Io9AWy+/b/wVWIbjigHCbd+zWLpPi8NnK0OFU+U3YCpP+KWfUvEnG5pFhltA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -100,13 +100,13 @@ } }, "node_modules/@ai-sdk/openai-compatible": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.20.tgz", - "integrity": "sha512-RsPg+HilsKc/d+jy4zItG7v7RJQm+caUu+iU0a5bx3eyyX5tkayMGDRklXMMMAPa4l34luzQwT3t4cmeHfdiWw==", + "version": "3.0.30", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai-compatible/-/openai-compatible-3.0.30.tgz", + "integrity": "sha512-BB35G4fS/Ey5OHbWrVLxRLX1gkTlO+9I4YhlmdH1skNVoPaFXZlR6bQ+1C76d/ug7O6ocbIw4qcd2G4+GPdWEA==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -116,9 +116,9 @@ } }, "node_modules/@ai-sdk/provider": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.4.tgz", - "integrity": "sha512-tbHKNLirllUNF3ZlkCsXnwab2ZV1Sl4b1H/Cp9ruCce15IBmskE8Gwkk0yo9xDWY+jho2of7lVXtwSsyrq7cwQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-4.0.7.tgz", + "integrity": "sha512-6or44XprPzKbr8zkmzosowSE0pxkvJcoojBL+mCZvPUt3kvXp3XSNqeVun9golb1acEfSo6yaEBRT18h2VU+1Q==", "license": "Apache-2.0", "dependencies": { "json-schema": "^0.4.0" @@ -128,12 +128,12 @@ } }, "node_modules/@ai-sdk/provider-utils": { - "version": "5.0.18", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.18.tgz", - "integrity": "sha512-UBNCrkxS5llgN2/RXLBRkjCFTIuL6YB1Goq5c+yRecnznhtX4XPjTwLHz2hrsTltTVZ0rqVegtnwFXdPBkjDHA==", + "version": "5.0.27", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-5.0.27.tgz", + "integrity": "sha512-EzAn4pdgG5g0xXtH6lE2zyNmfjDQIDjATkfqzuidEI35g++hh4+07vnjzkT/RmGmIClPZiRj/Q2GMPV2V7mkHw==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "4.0.4", + "@ai-sdk/provider": "4.0.7", "@standard-schema/spec": "^1.1.0", "@workflow/serde": "4.1.0", "eventsource-parser": "^3.0.8", @@ -147,9 +147,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -164,9 +164,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -181,9 +181,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -198,9 +198,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -215,9 +215,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -232,9 +232,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -283,9 +283,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -300,9 +300,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -334,9 +334,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -351,9 +351,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -368,9 +368,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -385,9 +385,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -402,9 +402,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -419,9 +419,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -436,9 +436,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -453,9 +453,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -470,9 +470,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -487,9 +487,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -504,9 +504,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -521,9 +521,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -538,9 +538,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -555,9 +555,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -572,9 +572,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -890,9 +890,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", - "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "version": "26.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", "dev": true, "license": "MIT", "dependencies": { @@ -942,17 +942,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -965,7 +965,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", + "@typescript-eslint/parser": "^8.67.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -981,16 +981,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3" }, "engines": { @@ -1006,14 +1006,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", "debug": "^4.4.3" }, "engines": { @@ -1028,14 +1028,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1046,9 +1046,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", "dev": true, "license": "MIT", "engines": { @@ -1063,15 +1063,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1088,9 +1088,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", "dev": true, "license": "MIT", "engines": { @@ -1102,16 +1102,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1130,16 +1130,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1154,13 +1154,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/types": "8.67.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1223,14 +1223,14 @@ } }, "node_modules/ai": { - "version": "7.0.48", - "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.48.tgz", - "integrity": "sha512-QmqshWFEDdkFFqSgdJ4cogaoDIYaedqbv73q/NXEYNkSIocJCzXnGFVyM9lrtAGTSBfm+5hq3/292ooXJ1jDSw==", + "version": "7.0.66", + "resolved": "https://registry.npmjs.org/ai/-/ai-7.0.66.tgz", + "integrity": "sha512-wBUyoCYF3GVr+62nelBgR8YbpTSsMZrzFyOOjiwijylNSM2TFCW35C+Pml2vc59/WLMpyhS/LWZ55M+B9DAcSg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/gateway": "4.0.37", - "@ai-sdk/provider": "4.0.4", - "@ai-sdk/provider-utils": "5.0.18" + "@ai-sdk/gateway": "4.0.52", + "@ai-sdk/provider": "4.0.7", + "@ai-sdk/provider-utils": "5.0.27" }, "engines": { "node": ">=22" @@ -1331,9 +1331,9 @@ } }, "node_modules/body-parser/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "engines": { "node": ">=18" @@ -1622,9 +1622,9 @@ } }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1635,32 +1635,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escape-html": { @@ -1683,9 +1683,9 @@ } }, "node_modules/eslint": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", - "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -1847,9 +1847,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", - "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -3048,9 +3048,9 @@ } }, "node_modules/tsx": { - "version": "4.23.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.4.tgz", - "integrity": "sha512-ZiUQ8oT/KzN51mJUWPqARYqwFLFJZtGZipRkw1ynHMr9vy3eU77m5yfF3Gzm6meEg/beW+lUu3fHYgskTN2oVQ==", + "version": "4.23.12", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz", + "integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3098,9 +3098,9 @@ } }, "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", "license": "MIT", "engines": { "node": ">=18" @@ -3125,16 +3125,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", - "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "version": "8.67.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0" + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/package.json b/package.json index 277f8f9..ac7cb0b 100644 --- a/package.json +++ b/package.json @@ -13,14 +13,15 @@ "prettier:check": "npx prettier --check .", "prettier:write": "npx prettier --write .", "start": "node dist/index.js", + "test": "npx tsc --noEmit -p tsconfig.test.json && tsx --test test/*.test.ts", "tsc": "npx tsc --noEmit" }, "dependencies": { - "@ai-sdk/anthropic": "^4.0.27", - "@ai-sdk/google": "^4.0.31", - "@ai-sdk/openai": "^4.0.27", - "@ai-sdk/openai-compatible": "^3.0.20", - "ai": "^7.0.48", + "@ai-sdk/anthropic": "^4.0.39", + "@ai-sdk/google": "^4.0.44", + "@ai-sdk/openai": "^4.0.42", + "@ai-sdk/openai-compatible": "^3.0.30", + "ai": "^7.0.66", "cors": "^2.8.6", "debug": "^4.4.3", "dotenv": "^17.4.2", @@ -32,13 +33,13 @@ "@types/cors": "^2.8.19", "@types/debug": "^4.1.13", "@types/express": "^5.0.6", - "@types/node": "^26.1.2", + "@types/node": "^26.2.0", "@types/picomatch": "^4.0.3", - "eslint": "^10.8.0", + "eslint": "^10.8.1", "nodemon": "^3.1.14", "prettier": "^3.9.6", - "tsx": "^4.23.4", + "tsx": "^4.23.12", "typescript": "^6.0.3", - "typescript-eslint": "^8.65.0" + "typescript-eslint": "^8.67.0" } } diff --git a/src/index.ts b/src/index.ts index 3361912..62f9050 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,8 +59,8 @@ console.log('maxOutputTokens:', maxOutputTokens) const disableInstructions = getBool(process.env.DISABLE_CLIENT_INSTRUCTIONS) console.log('disableInstructions:', disableInstructions) -process.env.INSTRUCTIONS_CHAT = process.env.INSTRUCTIONS_CHAT || process.env.INSTRUCTIONS // NOSONAR -console.log('INSTRUCTIONS_CHAT:', process.env.INSTRUCTIONS_CHAT) +const instructionsChat = process.env.INSTRUCTIONS_CHAT || process.env.INSTRUCTIONS // NOSONAR +console.log('INSTRUCTIONS_CHAT:', instructionsChat) console.log('INSTRUCTIONS_COMPLETION:', process.env.INSTRUCTIONS_COMPLETION) console.log('INSTRUCTIONS_OBJECT:', process.env.INSTRUCTIONS_OBJECT) @@ -92,8 +92,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { const result = streamText({ model: model, messages: modelMessages, - instructions: - (!disableInstructions && (instructions || system)) || process.env.INSTRUCTIONS_CHAT, + instructions: (!disableInstructions && (instructions || system)) || instructionsChat, maxOutputTokens, providerOptions, onError: onStreamError, @@ -102,7 +101,7 @@ app.post(['/', '/chat'], async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - pipeUIMessageStreamToResponse({ response: res, stream }) + return pipeUIMessageStreamToResponse({ response: res, stream }) }) app.post('/completion', async (req: Request, res: Response) => { @@ -124,7 +123,7 @@ app.post('/completion', async (req: Request, res: Response) => { writer.merge(toUIMessageStream({ stream: result.stream })) }, }) - pipeUIMessageStreamToResponse({ + return pipeUIMessageStreamToResponse({ response: res, stream, consumeSseStream: consumeStream, @@ -134,7 +133,8 @@ app.post('/completion', async (req: Request, res: Response) => { app.post('/object', async (req: Request, res: Response) => { const { instructions, output, prompt, system } = req.body debug('instructions:', (instructions || system)?.length) - debug('output:', output?.length) + // NOTE: output is an Object due to - app.use(express.json({ limit: '10mb' })) + // debug('output:', output?.length) debug('prompt:', prompt?.length) const result = streamText({ model: model, @@ -147,7 +147,10 @@ app.post('/object', async (req: Request, res: Response) => { onError: onStreamError, onEnd: onStreamEnd, }) - pipeTextStreamToResponse({ + // NOTE: pipe the raw stream - erroring the stream here would create an unhandled + // rejection (crashing the process) and pipeTextStreamToResponse already sent a 200. + // Model errors are still logged via onError and the stream simply ends. + return pipeTextStreamToResponse({ response: res, stream: toTextStream({ stream: result.stream }), }) @@ -182,11 +185,27 @@ function getModel() { if (!process.env.ANTHROPIC_API_KEY) throw new Error('Missing ANTHROPIC_API_KEY') return anthropic(process.env.MODEL) } else { + const isDefaultZen = + baseURL === 'https://opencode.ai/zen/v1' && + !process.env.MODEL && + !process.env.PROVIDER_API_KEY + debug('Applying Default Zen Headers:', isDefaultZen) const provider = createOpenAICompatible({ name: 'zen', baseURL: baseURL, apiKey: process.env.PROVIDER_API_KEY, includeUsage: true, + ...(isDefaultZen + ? { + headers: { + 'x-opencode-project': 'chat-server', + 'x-opencode-session': 'ses_chat-server', + 'x-opencode-request': 'chat-server', + 'x-opencode-client': 'opencode-tui', + 'User-Agent': 'opencode/1.14.50', + }, + } + : {}), }) return provider(process.env.MODEL || 'big-pickle') // NOSONAR } diff --git a/test/object.test.ts b/test/object.test.ts new file mode 100644 index 0000000..ea02354 --- /dev/null +++ b/test/object.test.ts @@ -0,0 +1,108 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' + +const BASE_URL = process.env.TEST_BASE_URL ?? 'http://localhost:3000' + +const nameAgeSchema = { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number' }, + }, + required: ['name', 'age'], +} + +test( + 'POST /object returns a JSON object matching the provided schema', + { timeout: 60_000 }, + async () => { + const response = await fetch(`${BASE_URL}/object`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instructions: 'You are a helpful assistant. Return only valid JSON.', + prompt: 'Extract the name and age from: John is 30 years old.', + output: nameAgeSchema, + }), + }) + + assert.equal(response.status, 200) + assert.match(response.headers.get('content-type') ?? '', /^text\/plain/) + + const body = await response.text() + assert.ok(body.length > 0, 'response body should not be empty') + + let parsed: unknown + assert.doesNotThrow(() => { + parsed = JSON.parse(body) + }) + + const object = parsed as { name?: string; age?: number } + assert.equal(typeof object.name, 'string') + assert.ok(object.name!.length > 0, 'name should not be empty') + assert.equal(typeof object.age, 'number') + assert.ok(object.age! > 0, 'age should be a positive number') + }, +) + +test( + 'POST /object without an output schema returns valid JSON', + { timeout: 60_000 }, + async () => { + const response = await fetch(`${BASE_URL}/object`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instructions: 'You are a helpful assistant. Return only valid JSON.', + prompt: 'Return a JSON object with a single key "ok" set to true.', + }), + }) + + assert.equal(response.status, 200) + const body = await response.text() + assert.ok(body.length > 0, 'response body should not be empty') + assert.doesNotThrow(() => JSON.parse(body)) + }, +) + +test('POST /object streams the response incrementally', { timeout: 60_000 }, async () => { + const response = await fetch(`${BASE_URL}/object`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + instructions: 'You are a helpful assistant. Return only valid JSON.', + prompt: + 'Return a JSON object with a single key "list" containing an array of the numbers 1 through 20.', + output: { + type: 'object', + properties: { + list: { type: 'array', items: { type: 'number' } }, + }, + required: ['list'], + }, + }), + }) + + assert.equal(response.status, 200) + assert.ok(response.body, 'response should have a body stream') + + const chunks: string[] = [] + let text = '' + const reader = response.body!.getReader() + while (true) { + const { done, value } = await reader.read() + if (done) break + chunks.push(new TextDecoder().decode(value)) + text += chunks[chunks.length - 1] + } + + assert.ok(chunks.length >= 1, 'response should be delivered in stream chunks') + assert.ok(text.length > 0, 'streamed body should not be empty') + + let parsed: unknown + assert.doesNotThrow(() => { + parsed = JSON.parse(text) + }) + const object = parsed as { list?: unknown[] } + assert.ok(Array.isArray(object.list), 'list should be an array') +}) From 02938b0263f01f47a2cebcf74bcea47b40586ee4 Mon Sep 17 00:00:00 2001 From: Shane <6071159+smashedr@users.noreply.github.com> Date: Sun, 16 Aug 2026 12:01:07 -0700 Subject: [PATCH 27/27] Add tsconfig.test.json --- tsconfig.test.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tsconfig.test.json diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..18ebdf6 --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true + }, + "include": ["src/**/*", "test/**/*"] +}