@kb-labs/adapters-vibeproxy 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # @kb-labs/adapters-vibeproxy
2
+
3
+ > Part of [KB Labs](https://github.com/KirillBaranov/kb-labs) ecosystem. Works exclusively within KB Labs platform.
4
+
5
+ VibeProxy local adapter supporting multiple LLM providers (Claude, GPT, etc.) through a unified interface.
6
+
7
+ ## Overview
8
+
9
+ | Property | Value |
10
+ |----------|-------|
11
+ | **Implements** | `ILLM` |
12
+ | **Type** | `core` |
13
+ | **Requires** | None |
14
+ | **Category** | AI |
15
+
16
+ ## Features
17
+
18
+ - **Multi-Provider** - Claude, GPT, Gemini, and more via single interface
19
+ - **Local Proxy** - Route through local VibeProxy server
20
+ - **Function Calling** - Native tool support for all providers
21
+ - **Model Switching** - Change provider by just changing model name
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pnpm add @kb-labs/adapters-vibeproxy
27
+ ```
28
+
29
+ ## Configuration
30
+
31
+ Add to your `kb.config.json`:
32
+
33
+ ```json
34
+ {
35
+ "platform": {
36
+ "adapters": {
37
+ "llm": "@kb-labs/adapters-vibeproxy"
38
+ },
39
+ "adapterOptions": {
40
+ "llm": {
41
+ "baseURL": "http://localhost:8317",
42
+ "apiKey": "any-string",
43
+ "model": "claude-sonnet-4-20250514",
44
+ "timeout": 120000
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Options
52
+
53
+ | Option | Type | Default | Description |
54
+ |--------|------|---------|-------------|
55
+ | `baseURL` | `string` | `"http://localhost:8317"` | VibeProxy server URL |
56
+ | `apiKey` | `string` | `"any-string"` | API key (any string works for local) |
57
+ | `model` | `string` | `"claude-sonnet-4-20250514"` | Model to use |
58
+ | `timeout` | `number` | `120000` | Request timeout in ms |
59
+
60
+ ## Usage
61
+
62
+ ### Via Platform (Recommended)
63
+
64
+ ```typescript
65
+ import { usePlatform } from '@kb-labs/sdk';
66
+
67
+ const platform = usePlatform();
68
+
69
+ // Chat with Claude
70
+ const response = await platform.llm.chat([
71
+ { role: 'user', content: 'Hello!' }
72
+ ]);
73
+
74
+ // Switch to GPT by changing model
75
+ const gptResponse = await platform.llm.chat(
76
+ [{ role: 'user', content: 'Hello!' }],
77
+ { model: 'gpt-4-turbo' }
78
+ );
79
+
80
+ // Function calling
81
+ const result = await platform.llm.chatWithTools(
82
+ [{ role: 'user', content: 'What time is it?' }],
83
+ [{ name: 'getTime', parameters: { ... } }]
84
+ );
85
+ ```
86
+
87
+ ### Standalone (Testing/Development)
88
+
89
+ ```typescript
90
+ import { createAdapter } from '@kb-labs/adapters-vibeproxy';
91
+
92
+ const llm = createAdapter({
93
+ baseURL: 'http://localhost:8317',
94
+ model: 'claude-sonnet-4-20250514'
95
+ });
96
+
97
+ const response = await llm.chat([
98
+ { role: 'user', content: 'Hello!' }
99
+ ]);
100
+ ```
101
+
102
+ ## Adapter Manifest
103
+
104
+ ```typescript
105
+ {
106
+ id: 'vibeproxy-llm',
107
+ name: 'VibeProxy LLM',
108
+ version: '0.1.0',
109
+ implements: 'ILLM',
110
+ capabilities: {
111
+ streaming: false, // TODO: implement SSE streaming
112
+ custom: {
113
+ functionCalling: true,
114
+ multiProvider: true,
115
+ },
116
+ },
117
+ }
118
+ ```
119
+
120
+ ## Supported Models
121
+
122
+ | Provider | Model Examples |
123
+ |----------|---------------|
124
+ | **Anthropic** | `claude-sonnet-4-20250514`, `claude-3-opus-*` |
125
+ | **OpenAI** | `gpt-4-turbo`, `gpt-3.5-turbo` |
126
+ | **Google** | `gemini-pro`, `gemini-ultra` |
127
+
128
+ ## FAQ
129
+
130
+ <details>
131
+ <summary><strong>Q: How do I start VibeProxy locally?</strong></summary>
132
+
133
+ See VibeProxy documentation for setup instructions. Default port is 8317.
134
+ </details>
135
+
136
+ <details>
137
+ <summary><strong>Q: Why use VibeProxy instead of direct API?</strong></summary>
138
+
139
+ - Single interface for multiple providers
140
+ - Local caching and rate limiting
141
+ - Request logging and analytics
142
+ - Cost tracking across providers
143
+ </details>
144
+
145
+ <details>
146
+ <summary><strong>Q: Is streaming supported?</strong></summary>
147
+
148
+ Not yet. Streaming (SSE) is planned for a future release.
149
+ </details>
150
+
151
+ ## Related Adapters
152
+
153
+ | Adapter | Use Case |
154
+ |---------|----------|
155
+ | `@kb-labs/adapters-openai` | Direct OpenAI API access |
156
+
157
+ ## License
158
+
159
+ [KB Public License v1.1](../../LICENSE) - KB Labs Team
@@ -0,0 +1,14 @@
1
+ export { VibeProxyLLM, VibeProxyLLMConfig, default as createAdapter, default } from './llm.js';
2
+ import { AdapterManifest } from '@kb-labs/core-platform';
3
+
4
+ /**
5
+ * @module @kb-labs/adapters-vibeproxy/manifest
6
+ * Adapter manifest for VibeProxy LLM.
7
+ */
8
+
9
+ /**
10
+ * Adapter manifest for VibeProxy LLM.
11
+ */
12
+ declare const manifest: AdapterManifest;
13
+
14
+ export { manifest };
package/dist/index.js ADDED
@@ -0,0 +1,307 @@
1
+ // src/llm.ts
2
+ var DEFAULT_MAX_TOKENS = 16384;
3
+ var VibeProxyLLM = class {
4
+ baseURL;
5
+ apiKey;
6
+ defaultModel;
7
+ timeout;
8
+ defaultMaxTokens;
9
+ constructor(config = {}) {
10
+ this.baseURL = config.baseURL ?? process.env.VIBEPROXY_URL ?? "http://localhost:8317";
11
+ this.apiKey = config.apiKey ?? process.env.VIBEPROXY_API_KEY ?? "any-string";
12
+ this.defaultModel = config.defaultModel ?? process.env.VIBEPROXY_MODEL ?? "claude-sonnet-4-20250514";
13
+ this.timeout = config.timeout ?? 12e4;
14
+ this.defaultMaxTokens = config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS;
15
+ }
16
+ getProtocolCapabilities() {
17
+ return {
18
+ cache: {
19
+ supported: true,
20
+ protocol: "explicit_handle",
21
+ scopes: ["prefix", "segments", "full_request"]
22
+ },
23
+ // Current implementation yields complete() as one chunk (not true streaming)
24
+ stream: { supported: false }
25
+ };
26
+ }
27
+ /**
28
+ * Make a request to VibeProxy.
29
+ */
30
+ async request(endpoint, body, headers) {
31
+ const controller = new AbortController();
32
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
33
+ try {
34
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
35
+ method: "POST",
36
+ headers: {
37
+ "Content-Type": "application/json",
38
+ "x-api-key": this.apiKey,
39
+ ...headers
40
+ },
41
+ body: JSON.stringify(body),
42
+ signal: controller.signal
43
+ });
44
+ if (!response.ok) {
45
+ const errorBody = await response.json().catch(() => ({ error: { message: response.statusText } }));
46
+ throw new Error(
47
+ `VibeProxy error: ${errorBody.error?.message ?? response.statusText}`
48
+ );
49
+ }
50
+ return response.json();
51
+ } finally {
52
+ clearTimeout(timeoutId);
53
+ }
54
+ }
55
+ buildExecutionHeaders(execution) {
56
+ const headers = {};
57
+ const cache = execution?.cache;
58
+ if (!cache) {
59
+ return headers;
60
+ }
61
+ headers["x-kb-cache-mode"] = cache.mode ?? "prefer";
62
+ if (cache.scope) {
63
+ headers["x-kb-cache-scope"] = cache.scope;
64
+ }
65
+ if (typeof cache.ttlSec === "number") {
66
+ headers["x-kb-cache-ttl-sec"] = String(cache.ttlSec);
67
+ }
68
+ if (cache.key) {
69
+ headers["x-kb-cache-key"] = cache.key;
70
+ }
71
+ return headers;
72
+ }
73
+ buildTraceHeaders(options) {
74
+ const trace = options?.metadata?.cacheDecisionTrace;
75
+ if (!trace) {
76
+ return {};
77
+ }
78
+ return {
79
+ "x-kb-cache-decision": JSON.stringify(trace)
80
+ };
81
+ }
82
+ extractTextContent(response) {
83
+ return response.content.filter((c) => c.type === "text").map((c) => c.text).join("");
84
+ }
85
+ convertMessagesForTools(messages) {
86
+ const vibeProxyMessages = [];
87
+ let systemPrompt;
88
+ for (const msg of messages) {
89
+ if (msg.role === "system") {
90
+ systemPrompt = msg.content;
91
+ continue;
92
+ }
93
+ if (msg.role === "tool") {
94
+ vibeProxyMessages.push({
95
+ role: "user",
96
+ content: [
97
+ {
98
+ type: "tool_result",
99
+ tool_use_id: msg.toolCallId || "",
100
+ content: msg.content
101
+ }
102
+ ]
103
+ });
104
+ continue;
105
+ }
106
+ if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
107
+ vibeProxyMessages.push({
108
+ role: "assistant",
109
+ content: this.toAssistantToolContent(msg)
110
+ });
111
+ continue;
112
+ }
113
+ vibeProxyMessages.push({
114
+ role: msg.role,
115
+ content: msg.content
116
+ });
117
+ }
118
+ return { messages: vibeProxyMessages, systemPrompt };
119
+ }
120
+ toAssistantToolContent(message) {
121
+ const contentBlocks = [];
122
+ if (message.content && message.content.trim()) {
123
+ contentBlocks.push({
124
+ type: "text",
125
+ text: message.content
126
+ });
127
+ }
128
+ for (const toolCall of message.toolCalls ?? []) {
129
+ contentBlocks.push({
130
+ type: "tool_use",
131
+ id: toolCall.id,
132
+ name: toolCall.name,
133
+ input: toolCall.input
134
+ });
135
+ }
136
+ return contentBlocks;
137
+ }
138
+ buildToolChoice(toolChoice) {
139
+ if (toolChoice === "auto") {
140
+ return { type: "auto" };
141
+ }
142
+ if (toolChoice === "required") {
143
+ return { type: "any" };
144
+ }
145
+ if (toolChoice && typeof toolChoice === "object") {
146
+ return { type: "tool", name: toolChoice.function.name };
147
+ }
148
+ return void 0;
149
+ }
150
+ extractToolCalls(response) {
151
+ return response.content.filter(
152
+ (c) => c.type === "tool_use"
153
+ ).map((tc) => ({
154
+ id: tc.id,
155
+ name: tc.name,
156
+ input: tc.input
157
+ }));
158
+ }
159
+ async complete(prompt, options) {
160
+ const model = options?.model ?? this.defaultModel;
161
+ const requestBody = {
162
+ model,
163
+ max_tokens: options?.maxTokens ?? this.defaultMaxTokens,
164
+ messages: [{ role: "user", content: prompt }]
165
+ };
166
+ if (options?.systemPrompt) {
167
+ requestBody.system = options.systemPrompt;
168
+ }
169
+ if (options?.temperature !== void 0) {
170
+ requestBody.temperature = options.temperature;
171
+ }
172
+ if (options?.stop) {
173
+ requestBody.stop_sequences = options.stop;
174
+ }
175
+ const response = await this.request(
176
+ "/v1/messages",
177
+ requestBody,
178
+ {
179
+ ...this.buildExecutionHeaders(options?.execution),
180
+ ...this.buildTraceHeaders(options)
181
+ }
182
+ );
183
+ return {
184
+ content: this.extractTextContent(response),
185
+ usage: {
186
+ promptTokens: response.usage.input_tokens,
187
+ completionTokens: response.usage.output_tokens,
188
+ cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,
189
+ cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,
190
+ providerUsage: {
191
+ stopReason: response.stop_reason
192
+ }
193
+ },
194
+ model: response.model
195
+ };
196
+ }
197
+ async *stream(prompt, options) {
198
+ const response = await this.complete(prompt, options);
199
+ yield response.content;
200
+ }
201
+ /**
202
+ * Chat with native tool calling support.
203
+ */
204
+ async chatWithTools(messages, options) {
205
+ const model = options?.model ?? this.defaultModel;
206
+ const converted = this.convertMessagesForTools(messages);
207
+ const tools = options.tools.map(
208
+ (tool) => ({
209
+ name: tool.name,
210
+ description: tool.description,
211
+ input_schema: tool.inputSchema
212
+ })
213
+ );
214
+ const toolChoice = this.buildToolChoice(options.toolChoice);
215
+ const requestBody = {
216
+ model,
217
+ max_tokens: options?.maxTokens ?? this.defaultMaxTokens,
218
+ messages: converted.messages,
219
+ tools: options.toolChoice !== "none" ? tools : void 0,
220
+ tool_choice: options.toolChoice !== "none" ? toolChoice : void 0
221
+ };
222
+ if (converted.systemPrompt || options?.systemPrompt) {
223
+ requestBody.system = converted.systemPrompt ?? options.systemPrompt;
224
+ }
225
+ if (options?.temperature !== void 0) {
226
+ requestBody.temperature = options.temperature;
227
+ }
228
+ if (options?.stop) {
229
+ requestBody.stop_sequences = options.stop;
230
+ }
231
+ const response = await this.request(
232
+ "/v1/messages",
233
+ requestBody,
234
+ {
235
+ ...this.buildExecutionHeaders(options?.execution),
236
+ ...this.buildTraceHeaders(options)
237
+ }
238
+ );
239
+ const textContent = this.extractTextContent(response);
240
+ const toolCalls = this.extractToolCalls(response);
241
+ return {
242
+ content: textContent,
243
+ usage: {
244
+ promptTokens: response.usage.input_tokens,
245
+ completionTokens: response.usage.output_tokens,
246
+ cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,
247
+ cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,
248
+ providerUsage: {
249
+ stopReason: response.stop_reason
250
+ }
251
+ },
252
+ model: response.model,
253
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
254
+ stopReason: response.stop_reason
255
+ };
256
+ }
257
+ };
258
+ function createAdapter(config) {
259
+ return new VibeProxyLLM(config);
260
+ }
261
+
262
+ // src/manifest.ts
263
+ var manifest = {
264
+ manifestVersion: "1.0.0",
265
+ id: "vibeproxy-llm",
266
+ name: "VibeProxy LLM",
267
+ version: "0.1.0",
268
+ description: "VibeProxy local adapter supporting multiple LLM providers (Claude, GPT, etc.)",
269
+ author: "KB Labs Team",
270
+ license: "KBPL-1.1",
271
+ type: "core",
272
+ implements: "ILLM",
273
+ capabilities: {
274
+ streaming: false,
275
+ // TODO: implement SSE streaming
276
+ custom: {
277
+ functionCalling: true,
278
+ multiProvider: true
279
+ }
280
+ },
281
+ configSchema: {
282
+ baseURL: {
283
+ type: "string",
284
+ default: "http://localhost:8317",
285
+ description: "VibeProxy server URL"
286
+ },
287
+ apiKey: {
288
+ type: "string",
289
+ default: "any-string",
290
+ description: "API key (any string works for local VibeProxy)"
291
+ },
292
+ model: {
293
+ type: "string",
294
+ default: "claude-sonnet-4-20250514",
295
+ description: "Model to use (claude-*, gpt-*, etc.)"
296
+ },
297
+ timeout: {
298
+ type: "number",
299
+ default: 12e4,
300
+ description: "Request timeout in milliseconds"
301
+ }
302
+ }
303
+ };
304
+
305
+ export { VibeProxyLLM, createAdapter, createAdapter as default, manifest };
306
+ //# sourceMappingURL=index.js.map
307
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../Users/kirillbaranov/Desktop/kb-labs-workspace/infra/kb-labs-adapters/packages/adapters-vibeproxy/src/llm.ts","../../../../../../Users/kirillbaranov/Desktop/kb-labs-workspace/infra/kb-labs-adapters/packages/adapters-vibeproxy/src/manifest.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/adapters-vibeproxy/llm\n * VibeProxy implementation of ILLM interface.\n * Connects to local VibeProxy server (supports Claude, GPT, and other models).\n */\n\nimport type {\n ILLM,\n LLMOptions,\n LLMResponse,\n LLMMessage,\n LLMToolCallOptions,\n LLMToolCallResponse,\n LLMTool,\n LLMToolCall,\n LLMProtocolCapabilities,\n LLMExecutionPolicy,\n} from \"@kb-labs/core-platform\";\n\n/**\n * Configuration for VibeProxy LLM adapter.\n */\nexport interface VibeProxyLLMConfig {\n /** Base URL for VibeProxy (defaults to http://localhost:8317) */\n baseURL?: string;\n /** API key (can be any string for VibeProxy) */\n apiKey?: string;\n /** Default model to use */\n defaultModel?: string;\n /** Request timeout in ms (defaults to 120000) */\n timeout?: number;\n /** Default max output tokens when caller does not specify. Overrides the hardcoded 4096. */\n defaultMaxTokens?: number;\n}\n\n/** Default max output tokens — avoids hitting the 4096 hardcoded limit mid-response. */\nconst DEFAULT_MAX_TOKENS = 16_384;\n\n/**\n * VibeProxy Messages API request format.\n */\ninterface VibeProxyMessagesRequest {\n model: string;\n max_tokens: number;\n messages: Array<{\n role: \"user\" | \"assistant\";\n content:\n | string\n | Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n | { type: \"tool_result\"; tool_use_id: string; content: string }\n >;\n }>;\n system?: string;\n temperature?: number;\n stop_sequences?: string[];\n tools?: Array<{\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n }>;\n tool_choice?:\n | { type: \"auto\" }\n | { type: \"any\" }\n | { type: \"tool\"; name: string };\n}\n\n/**\n * VibeProxy Messages API response format.\n */\ninterface VibeProxyMessagesResponse {\n id: string;\n type: \"message\";\n role: \"assistant\";\n model: string;\n content: Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n >;\n stop_reason: \"end_turn\" | \"max_tokens\" | \"stop_sequence\" | \"tool_use\";\n stop_sequence: string | null;\n usage: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n };\n}\n\n/**\n * VibeProxy implementation of ILLM interface.\n * Supports multiple model providers through local VibeProxy server.\n */\nexport class VibeProxyLLM implements ILLM {\n private baseURL: string;\n private apiKey: string;\n private defaultModel: string;\n private timeout: number;\n private defaultMaxTokens: number;\n\n constructor(config: VibeProxyLLMConfig = {}) {\n this.baseURL =\n config.baseURL ?? process.env.VIBEPROXY_URL ?? \"http://localhost:8317\";\n this.apiKey =\n config.apiKey ?? process.env.VIBEPROXY_API_KEY ?? \"any-string\";\n this.defaultModel =\n config.defaultModel ??\n process.env.VIBEPROXY_MODEL ??\n \"claude-sonnet-4-20250514\";\n this.timeout = config.timeout ?? 120000;\n this.defaultMaxTokens = config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS;\n }\n\n getProtocolCapabilities(): LLMProtocolCapabilities {\n return {\n cache: {\n supported: true,\n protocol: \"explicit_handle\",\n scopes: [\"prefix\", \"segments\", \"full_request\"],\n },\n // Current implementation yields complete() as one chunk (not true streaming)\n stream: { supported: false },\n };\n }\n\n /**\n * Make a request to VibeProxy.\n */\n private async request<T>(\n endpoint: string,\n body: unknown,\n headers?: Record<string, string>,\n ): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.baseURL}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorBody = (await response\n .json()\n .catch(() => ({ error: { message: response.statusText } }))) as {\n error?: { message?: string };\n };\n throw new Error(\n `VibeProxy error: ${errorBody.error?.message ?? response.statusText}`,\n );\n }\n\n return response.json() as Promise<T>;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private buildExecutionHeaders(execution?: LLMExecutionPolicy): Record<string, string> {\n const headers: Record<string, string> = {};\n const cache = execution?.cache;\n if (!cache) {\n return headers;\n }\n\n headers[\"x-kb-cache-mode\"] = cache.mode ?? \"prefer\";\n if (cache.scope) {\n headers[\"x-kb-cache-scope\"] = cache.scope;\n }\n if (typeof cache.ttlSec === \"number\") {\n headers[\"x-kb-cache-ttl-sec\"] = String(cache.ttlSec);\n }\n if (cache.key) {\n headers[\"x-kb-cache-key\"] = cache.key;\n }\n\n return headers;\n }\n\n private buildTraceHeaders(options?: LLMOptions): Record<string, string> {\n const trace = options?.metadata?.cacheDecisionTrace;\n if (!trace) {\n return {};\n }\n return {\n \"x-kb-cache-decision\": JSON.stringify(trace),\n };\n }\n\n private extractTextContent(response: VibeProxyMessagesResponse): string {\n return response.content\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n }\n\n private convertMessagesForTools(\n messages: LLMMessage[],\n ): {\n messages: VibeProxyMessagesRequest[\"messages\"];\n systemPrompt?: string;\n } {\n const vibeProxyMessages: VibeProxyMessagesRequest[\"messages\"] = [];\n let systemPrompt: string | undefined;\n\n for (const msg of messages) {\n if (msg.role === \"system\") {\n systemPrompt = msg.content;\n continue;\n }\n if (msg.role === \"tool\") {\n vibeProxyMessages.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.toolCallId || \"\",\n content: msg.content,\n },\n ],\n });\n continue;\n }\n if (msg.role === \"assistant\" && msg.toolCalls && msg.toolCalls.length > 0) {\n vibeProxyMessages.push({\n role: \"assistant\",\n content: this.toAssistantToolContent(msg),\n });\n continue;\n }\n vibeProxyMessages.push({\n role: msg.role as \"user\" | \"assistant\",\n content: msg.content,\n });\n }\n\n return { messages: vibeProxyMessages, systemPrompt };\n }\n\n private toAssistantToolContent(\n message: LLMMessage,\n ): Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n > {\n const contentBlocks: Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n > = [];\n\n if (message.content && message.content.trim()) {\n contentBlocks.push({\n type: \"text\",\n text: message.content,\n });\n }\n\n for (const toolCall of message.toolCalls ?? []) {\n contentBlocks.push({\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input,\n });\n }\n return contentBlocks;\n }\n\n private buildToolChoice(\n toolChoice: LLMToolCallOptions[\"toolChoice\"],\n ): VibeProxyMessagesRequest[\"tool_choice\"] {\n if (toolChoice === \"auto\") {\n return { type: \"auto\" };\n }\n if (toolChoice === \"required\") {\n return { type: \"any\" };\n }\n if (toolChoice && typeof toolChoice === \"object\") {\n return { type: \"tool\", name: toolChoice.function.name };\n }\n return undefined;\n }\n\n private extractToolCalls(response: VibeProxyMessagesResponse): LLMToolCall[] {\n return response.content\n .filter(\n (\n c,\n ): c is {\n type: \"tool_use\";\n id: string;\n name: string;\n input: unknown;\n } => c.type === \"tool_use\",\n )\n .map((tc) => ({\n id: tc.id,\n name: tc.name,\n input: tc.input,\n }));\n }\n\n async complete(prompt: string, options?: LLMOptions): Promise<LLMResponse> {\n const model = options?.model ?? this.defaultModel;\n\n const requestBody: VibeProxyMessagesRequest = {\n model,\n max_tokens: options?.maxTokens ?? this.defaultMaxTokens,\n messages: [{ role: \"user\", content: prompt }],\n };\n\n if (options?.systemPrompt) {\n requestBody.system = options.systemPrompt;\n }\n if (options?.temperature !== undefined) {\n requestBody.temperature = options.temperature;\n }\n if (options?.stop) {\n requestBody.stop_sequences = options.stop;\n }\n\n const response = await this.request<VibeProxyMessagesResponse>(\n \"/v1/messages\",\n requestBody,\n {\n ...this.buildExecutionHeaders(options?.execution),\n ...this.buildTraceHeaders(options),\n },\n );\n\n return {\n content: this.extractTextContent(response),\n usage: {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,\n cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,\n providerUsage: {\n stopReason: response.stop_reason,\n },\n },\n model: response.model,\n };\n }\n\n async *stream(prompt: string, options?: LLMOptions): AsyncIterable<string> {\n // VibeProxy supports streaming via SSE, but for simplicity we'll use non-streaming\n // and yield the full response. Can be enhanced later with proper SSE support.\n const response = await this.complete(prompt, options);\n yield response.content;\n }\n\n /**\n * Chat with native tool calling support.\n */\n async chatWithTools(\n messages: LLMMessage[],\n options: LLMToolCallOptions,\n ): Promise<LLMToolCallResponse> {\n const model = options?.model ?? this.defaultModel;\n const converted = this.convertMessagesForTools(messages);\n\n // Convert LLMTool[] to VibeProxy tools format\n const tools: VibeProxyMessagesRequest[\"tools\"] = options.tools.map(\n (tool: LLMTool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: tool.inputSchema,\n }),\n );\n const toolChoice = this.buildToolChoice(options.toolChoice);\n\n const requestBody: VibeProxyMessagesRequest = {\n model,\n max_tokens: options?.maxTokens ?? this.defaultMaxTokens,\n messages: converted.messages,\n tools: options.toolChoice !== \"none\" ? tools : undefined,\n tool_choice: options.toolChoice !== \"none\" ? toolChoice : undefined,\n };\n\n if (converted.systemPrompt || options?.systemPrompt) {\n requestBody.system = converted.systemPrompt ?? options.systemPrompt;\n }\n if (options?.temperature !== undefined) {\n requestBody.temperature = options.temperature;\n }\n if (options?.stop) {\n requestBody.stop_sequences = options.stop;\n }\n\n const response = await this.request<VibeProxyMessagesResponse>(\n \"/v1/messages\",\n requestBody,\n {\n ...this.buildExecutionHeaders(options?.execution),\n ...this.buildTraceHeaders(options),\n },\n );\n const textContent = this.extractTextContent(response);\n const toolCalls = this.extractToolCalls(response);\n\n return {\n content: textContent,\n usage: {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,\n cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,\n providerUsage: {\n stopReason: response.stop_reason,\n },\n },\n model: response.model,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n stopReason: response.stop_reason,\n };\n }\n}\n\n/**\n * Create VibeProxy LLM adapter.\n * This is the factory function called by initPlatform() when loading adapters.\n */\nexport function createAdapter(config?: VibeProxyLLMConfig): VibeProxyLLM {\n return new VibeProxyLLM(config);\n}\n\n// Default export for direct import\nexport default createAdapter;\n","/**\n * @module @kb-labs/adapters-vibeproxy/manifest\n * Adapter manifest for VibeProxy LLM.\n */\n\nimport type { AdapterManifest } from \"@kb-labs/core-platform\";\n\n/**\n * Adapter manifest for VibeProxy LLM.\n */\nexport const manifest: AdapterManifest = {\n manifestVersion: \"1.0.0\",\n id: \"vibeproxy-llm\",\n name: \"VibeProxy LLM\",\n version: \"0.1.0\",\n description:\n \"VibeProxy local adapter supporting multiple LLM providers (Claude, GPT, etc.)\",\n author: \"KB Labs Team\",\n license: \"KBPL-1.1\",\n type: \"core\",\n implements: \"ILLM\",\n capabilities: {\n streaming: false, // TODO: implement SSE streaming\n custom: {\n functionCalling: true,\n multiProvider: true,\n },\n },\n configSchema: {\n baseURL: {\n type: \"string\",\n default: \"http://localhost:8317\",\n description: \"VibeProxy server URL\",\n },\n apiKey: {\n type: \"string\",\n default: \"any-string\",\n description: \"API key (any string works for local VibeProxy)\",\n },\n model: {\n type: \"string\",\n default: \"claude-sonnet-4-20250514\",\n description: \"Model to use (claude-*, gpt-*, etc.)\",\n },\n timeout: {\n type: \"number\",\n default: 120000,\n description: \"Request timeout in milliseconds\",\n },\n },\n};\n"]}
package/dist/llm.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { ILLM, LLMProtocolCapabilities, LLMOptions, LLMResponse, LLMMessage, LLMToolCallOptions, LLMToolCallResponse } from '@kb-labs/core-platform';
2
+
3
+ /**
4
+ * @module @kb-labs/adapters-vibeproxy/llm
5
+ * VibeProxy implementation of ILLM interface.
6
+ * Connects to local VibeProxy server (supports Claude, GPT, and other models).
7
+ */
8
+
9
+ /**
10
+ * Configuration for VibeProxy LLM adapter.
11
+ */
12
+ interface VibeProxyLLMConfig {
13
+ /** Base URL for VibeProxy (defaults to http://localhost:8317) */
14
+ baseURL?: string;
15
+ /** API key (can be any string for VibeProxy) */
16
+ apiKey?: string;
17
+ /** Default model to use */
18
+ defaultModel?: string;
19
+ /** Request timeout in ms (defaults to 120000) */
20
+ timeout?: number;
21
+ /** Default max output tokens when caller does not specify. Overrides the hardcoded 4096. */
22
+ defaultMaxTokens?: number;
23
+ }
24
+ /**
25
+ * VibeProxy implementation of ILLM interface.
26
+ * Supports multiple model providers through local VibeProxy server.
27
+ */
28
+ declare class VibeProxyLLM implements ILLM {
29
+ private baseURL;
30
+ private apiKey;
31
+ private defaultModel;
32
+ private timeout;
33
+ private defaultMaxTokens;
34
+ constructor(config?: VibeProxyLLMConfig);
35
+ getProtocolCapabilities(): LLMProtocolCapabilities;
36
+ /**
37
+ * Make a request to VibeProxy.
38
+ */
39
+ private request;
40
+ private buildExecutionHeaders;
41
+ private buildTraceHeaders;
42
+ private extractTextContent;
43
+ private convertMessagesForTools;
44
+ private toAssistantToolContent;
45
+ private buildToolChoice;
46
+ private extractToolCalls;
47
+ complete(prompt: string, options?: LLMOptions): Promise<LLMResponse>;
48
+ stream(prompt: string, options?: LLMOptions): AsyncIterable<string>;
49
+ /**
50
+ * Chat with native tool calling support.
51
+ */
52
+ chatWithTools(messages: LLMMessage[], options: LLMToolCallOptions): Promise<LLMToolCallResponse>;
53
+ }
54
+ /**
55
+ * Create VibeProxy LLM adapter.
56
+ * This is the factory function called by initPlatform() when loading adapters.
57
+ */
58
+ declare function createAdapter(config?: VibeProxyLLMConfig): VibeProxyLLM;
59
+
60
+ export { VibeProxyLLM, type VibeProxyLLMConfig, createAdapter, createAdapter as default };
package/dist/llm.js ADDED
@@ -0,0 +1,265 @@
1
+ // src/llm.ts
2
+ var DEFAULT_MAX_TOKENS = 16384;
3
+ var VibeProxyLLM = class {
4
+ baseURL;
5
+ apiKey;
6
+ defaultModel;
7
+ timeout;
8
+ defaultMaxTokens;
9
+ constructor(config = {}) {
10
+ this.baseURL = config.baseURL ?? process.env.VIBEPROXY_URL ?? "http://localhost:8317";
11
+ this.apiKey = config.apiKey ?? process.env.VIBEPROXY_API_KEY ?? "any-string";
12
+ this.defaultModel = config.defaultModel ?? process.env.VIBEPROXY_MODEL ?? "claude-sonnet-4-20250514";
13
+ this.timeout = config.timeout ?? 12e4;
14
+ this.defaultMaxTokens = config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS;
15
+ }
16
+ getProtocolCapabilities() {
17
+ return {
18
+ cache: {
19
+ supported: true,
20
+ protocol: "explicit_handle",
21
+ scopes: ["prefix", "segments", "full_request"]
22
+ },
23
+ // Current implementation yields complete() as one chunk (not true streaming)
24
+ stream: { supported: false }
25
+ };
26
+ }
27
+ /**
28
+ * Make a request to VibeProxy.
29
+ */
30
+ async request(endpoint, body, headers) {
31
+ const controller = new AbortController();
32
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
33
+ try {
34
+ const response = await fetch(`${this.baseURL}${endpoint}`, {
35
+ method: "POST",
36
+ headers: {
37
+ "Content-Type": "application/json",
38
+ "x-api-key": this.apiKey,
39
+ ...headers
40
+ },
41
+ body: JSON.stringify(body),
42
+ signal: controller.signal
43
+ });
44
+ if (!response.ok) {
45
+ const errorBody = await response.json().catch(() => ({ error: { message: response.statusText } }));
46
+ throw new Error(
47
+ `VibeProxy error: ${errorBody.error?.message ?? response.statusText}`
48
+ );
49
+ }
50
+ return response.json();
51
+ } finally {
52
+ clearTimeout(timeoutId);
53
+ }
54
+ }
55
+ buildExecutionHeaders(execution) {
56
+ const headers = {};
57
+ const cache = execution?.cache;
58
+ if (!cache) {
59
+ return headers;
60
+ }
61
+ headers["x-kb-cache-mode"] = cache.mode ?? "prefer";
62
+ if (cache.scope) {
63
+ headers["x-kb-cache-scope"] = cache.scope;
64
+ }
65
+ if (typeof cache.ttlSec === "number") {
66
+ headers["x-kb-cache-ttl-sec"] = String(cache.ttlSec);
67
+ }
68
+ if (cache.key) {
69
+ headers["x-kb-cache-key"] = cache.key;
70
+ }
71
+ return headers;
72
+ }
73
+ buildTraceHeaders(options) {
74
+ const trace = options?.metadata?.cacheDecisionTrace;
75
+ if (!trace) {
76
+ return {};
77
+ }
78
+ return {
79
+ "x-kb-cache-decision": JSON.stringify(trace)
80
+ };
81
+ }
82
+ extractTextContent(response) {
83
+ return response.content.filter((c) => c.type === "text").map((c) => c.text).join("");
84
+ }
85
+ convertMessagesForTools(messages) {
86
+ const vibeProxyMessages = [];
87
+ let systemPrompt;
88
+ for (const msg of messages) {
89
+ if (msg.role === "system") {
90
+ systemPrompt = msg.content;
91
+ continue;
92
+ }
93
+ if (msg.role === "tool") {
94
+ vibeProxyMessages.push({
95
+ role: "user",
96
+ content: [
97
+ {
98
+ type: "tool_result",
99
+ tool_use_id: msg.toolCallId || "",
100
+ content: msg.content
101
+ }
102
+ ]
103
+ });
104
+ continue;
105
+ }
106
+ if (msg.role === "assistant" && msg.toolCalls && msg.toolCalls.length > 0) {
107
+ vibeProxyMessages.push({
108
+ role: "assistant",
109
+ content: this.toAssistantToolContent(msg)
110
+ });
111
+ continue;
112
+ }
113
+ vibeProxyMessages.push({
114
+ role: msg.role,
115
+ content: msg.content
116
+ });
117
+ }
118
+ return { messages: vibeProxyMessages, systemPrompt };
119
+ }
120
+ toAssistantToolContent(message) {
121
+ const contentBlocks = [];
122
+ if (message.content && message.content.trim()) {
123
+ contentBlocks.push({
124
+ type: "text",
125
+ text: message.content
126
+ });
127
+ }
128
+ for (const toolCall of message.toolCalls ?? []) {
129
+ contentBlocks.push({
130
+ type: "tool_use",
131
+ id: toolCall.id,
132
+ name: toolCall.name,
133
+ input: toolCall.input
134
+ });
135
+ }
136
+ return contentBlocks;
137
+ }
138
+ buildToolChoice(toolChoice) {
139
+ if (toolChoice === "auto") {
140
+ return { type: "auto" };
141
+ }
142
+ if (toolChoice === "required") {
143
+ return { type: "any" };
144
+ }
145
+ if (toolChoice && typeof toolChoice === "object") {
146
+ return { type: "tool", name: toolChoice.function.name };
147
+ }
148
+ return void 0;
149
+ }
150
+ extractToolCalls(response) {
151
+ return response.content.filter(
152
+ (c) => c.type === "tool_use"
153
+ ).map((tc) => ({
154
+ id: tc.id,
155
+ name: tc.name,
156
+ input: tc.input
157
+ }));
158
+ }
159
+ async complete(prompt, options) {
160
+ const model = options?.model ?? this.defaultModel;
161
+ const requestBody = {
162
+ model,
163
+ max_tokens: options?.maxTokens ?? this.defaultMaxTokens,
164
+ messages: [{ role: "user", content: prompt }]
165
+ };
166
+ if (options?.systemPrompt) {
167
+ requestBody.system = options.systemPrompt;
168
+ }
169
+ if (options?.temperature !== void 0) {
170
+ requestBody.temperature = options.temperature;
171
+ }
172
+ if (options?.stop) {
173
+ requestBody.stop_sequences = options.stop;
174
+ }
175
+ const response = await this.request(
176
+ "/v1/messages",
177
+ requestBody,
178
+ {
179
+ ...this.buildExecutionHeaders(options?.execution),
180
+ ...this.buildTraceHeaders(options)
181
+ }
182
+ );
183
+ return {
184
+ content: this.extractTextContent(response),
185
+ usage: {
186
+ promptTokens: response.usage.input_tokens,
187
+ completionTokens: response.usage.output_tokens,
188
+ cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,
189
+ cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,
190
+ providerUsage: {
191
+ stopReason: response.stop_reason
192
+ }
193
+ },
194
+ model: response.model
195
+ };
196
+ }
197
+ async *stream(prompt, options) {
198
+ const response = await this.complete(prompt, options);
199
+ yield response.content;
200
+ }
201
+ /**
202
+ * Chat with native tool calling support.
203
+ */
204
+ async chatWithTools(messages, options) {
205
+ const model = options?.model ?? this.defaultModel;
206
+ const converted = this.convertMessagesForTools(messages);
207
+ const tools = options.tools.map(
208
+ (tool) => ({
209
+ name: tool.name,
210
+ description: tool.description,
211
+ input_schema: tool.inputSchema
212
+ })
213
+ );
214
+ const toolChoice = this.buildToolChoice(options.toolChoice);
215
+ const requestBody = {
216
+ model,
217
+ max_tokens: options?.maxTokens ?? this.defaultMaxTokens,
218
+ messages: converted.messages,
219
+ tools: options.toolChoice !== "none" ? tools : void 0,
220
+ tool_choice: options.toolChoice !== "none" ? toolChoice : void 0
221
+ };
222
+ if (converted.systemPrompt || options?.systemPrompt) {
223
+ requestBody.system = converted.systemPrompt ?? options.systemPrompt;
224
+ }
225
+ if (options?.temperature !== void 0) {
226
+ requestBody.temperature = options.temperature;
227
+ }
228
+ if (options?.stop) {
229
+ requestBody.stop_sequences = options.stop;
230
+ }
231
+ const response = await this.request(
232
+ "/v1/messages",
233
+ requestBody,
234
+ {
235
+ ...this.buildExecutionHeaders(options?.execution),
236
+ ...this.buildTraceHeaders(options)
237
+ }
238
+ );
239
+ const textContent = this.extractTextContent(response);
240
+ const toolCalls = this.extractToolCalls(response);
241
+ return {
242
+ content: textContent,
243
+ usage: {
244
+ promptTokens: response.usage.input_tokens,
245
+ completionTokens: response.usage.output_tokens,
246
+ cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,
247
+ cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,
248
+ providerUsage: {
249
+ stopReason: response.stop_reason
250
+ }
251
+ },
252
+ model: response.model,
253
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
254
+ stopReason: response.stop_reason
255
+ };
256
+ }
257
+ };
258
+ function createAdapter(config) {
259
+ return new VibeProxyLLM(config);
260
+ }
261
+ var llm_default = createAdapter;
262
+
263
+ export { VibeProxyLLM, createAdapter, llm_default as default };
264
+ //# sourceMappingURL=llm.js.map
265
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../Users/kirillbaranov/Desktop/kb-labs-workspace/infra/kb-labs-adapters/packages/adapters-vibeproxy/src/llm.ts"],"names":[],"mappings":"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","file":"llm.js","sourcesContent":["/**\n * @module @kb-labs/adapters-vibeproxy/llm\n * VibeProxy implementation of ILLM interface.\n * Connects to local VibeProxy server (supports Claude, GPT, and other models).\n */\n\nimport type {\n ILLM,\n LLMOptions,\n LLMResponse,\n LLMMessage,\n LLMToolCallOptions,\n LLMToolCallResponse,\n LLMTool,\n LLMToolCall,\n LLMProtocolCapabilities,\n LLMExecutionPolicy,\n} from \"@kb-labs/core-platform\";\n\n/**\n * Configuration for VibeProxy LLM adapter.\n */\nexport interface VibeProxyLLMConfig {\n /** Base URL for VibeProxy (defaults to http://localhost:8317) */\n baseURL?: string;\n /** API key (can be any string for VibeProxy) */\n apiKey?: string;\n /** Default model to use */\n defaultModel?: string;\n /** Request timeout in ms (defaults to 120000) */\n timeout?: number;\n /** Default max output tokens when caller does not specify. Overrides the hardcoded 4096. */\n defaultMaxTokens?: number;\n}\n\n/** Default max output tokens — avoids hitting the 4096 hardcoded limit mid-response. */\nconst DEFAULT_MAX_TOKENS = 16_384;\n\n/**\n * VibeProxy Messages API request format.\n */\ninterface VibeProxyMessagesRequest {\n model: string;\n max_tokens: number;\n messages: Array<{\n role: \"user\" | \"assistant\";\n content:\n | string\n | Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n | { type: \"tool_result\"; tool_use_id: string; content: string }\n >;\n }>;\n system?: string;\n temperature?: number;\n stop_sequences?: string[];\n tools?: Array<{\n name: string;\n description: string;\n input_schema: Record<string, unknown>;\n }>;\n tool_choice?:\n | { type: \"auto\" }\n | { type: \"any\" }\n | { type: \"tool\"; name: string };\n}\n\n/**\n * VibeProxy Messages API response format.\n */\ninterface VibeProxyMessagesResponse {\n id: string;\n type: \"message\";\n role: \"assistant\";\n model: string;\n content: Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n >;\n stop_reason: \"end_turn\" | \"max_tokens\" | \"stop_sequence\" | \"tool_use\";\n stop_sequence: string | null;\n usage: {\n input_tokens: number;\n output_tokens: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n };\n}\n\n/**\n * VibeProxy implementation of ILLM interface.\n * Supports multiple model providers through local VibeProxy server.\n */\nexport class VibeProxyLLM implements ILLM {\n private baseURL: string;\n private apiKey: string;\n private defaultModel: string;\n private timeout: number;\n private defaultMaxTokens: number;\n\n constructor(config: VibeProxyLLMConfig = {}) {\n this.baseURL =\n config.baseURL ?? process.env.VIBEPROXY_URL ?? \"http://localhost:8317\";\n this.apiKey =\n config.apiKey ?? process.env.VIBEPROXY_API_KEY ?? \"any-string\";\n this.defaultModel =\n config.defaultModel ??\n process.env.VIBEPROXY_MODEL ??\n \"claude-sonnet-4-20250514\";\n this.timeout = config.timeout ?? 120000;\n this.defaultMaxTokens = config.defaultMaxTokens ?? DEFAULT_MAX_TOKENS;\n }\n\n getProtocolCapabilities(): LLMProtocolCapabilities {\n return {\n cache: {\n supported: true,\n protocol: \"explicit_handle\",\n scopes: [\"prefix\", \"segments\", \"full_request\"],\n },\n // Current implementation yields complete() as one chunk (not true streaming)\n stream: { supported: false },\n };\n }\n\n /**\n * Make a request to VibeProxy.\n */\n private async request<T>(\n endpoint: string,\n body: unknown,\n headers?: Record<string, string>,\n ): Promise<T> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(`${this.baseURL}${endpoint}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorBody = (await response\n .json()\n .catch(() => ({ error: { message: response.statusText } }))) as {\n error?: { message?: string };\n };\n throw new Error(\n `VibeProxy error: ${errorBody.error?.message ?? response.statusText}`,\n );\n }\n\n return response.json() as Promise<T>;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private buildExecutionHeaders(execution?: LLMExecutionPolicy): Record<string, string> {\n const headers: Record<string, string> = {};\n const cache = execution?.cache;\n if (!cache) {\n return headers;\n }\n\n headers[\"x-kb-cache-mode\"] = cache.mode ?? \"prefer\";\n if (cache.scope) {\n headers[\"x-kb-cache-scope\"] = cache.scope;\n }\n if (typeof cache.ttlSec === \"number\") {\n headers[\"x-kb-cache-ttl-sec\"] = String(cache.ttlSec);\n }\n if (cache.key) {\n headers[\"x-kb-cache-key\"] = cache.key;\n }\n\n return headers;\n }\n\n private buildTraceHeaders(options?: LLMOptions): Record<string, string> {\n const trace = options?.metadata?.cacheDecisionTrace;\n if (!trace) {\n return {};\n }\n return {\n \"x-kb-cache-decision\": JSON.stringify(trace),\n };\n }\n\n private extractTextContent(response: VibeProxyMessagesResponse): string {\n return response.content\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n }\n\n private convertMessagesForTools(\n messages: LLMMessage[],\n ): {\n messages: VibeProxyMessagesRequest[\"messages\"];\n systemPrompt?: string;\n } {\n const vibeProxyMessages: VibeProxyMessagesRequest[\"messages\"] = [];\n let systemPrompt: string | undefined;\n\n for (const msg of messages) {\n if (msg.role === \"system\") {\n systemPrompt = msg.content;\n continue;\n }\n if (msg.role === \"tool\") {\n vibeProxyMessages.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.toolCallId || \"\",\n content: msg.content,\n },\n ],\n });\n continue;\n }\n if (msg.role === \"assistant\" && msg.toolCalls && msg.toolCalls.length > 0) {\n vibeProxyMessages.push({\n role: \"assistant\",\n content: this.toAssistantToolContent(msg),\n });\n continue;\n }\n vibeProxyMessages.push({\n role: msg.role as \"user\" | \"assistant\",\n content: msg.content,\n });\n }\n\n return { messages: vibeProxyMessages, systemPrompt };\n }\n\n private toAssistantToolContent(\n message: LLMMessage,\n ): Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n > {\n const contentBlocks: Array<\n | { type: \"text\"; text: string }\n | { type: \"tool_use\"; id: string; name: string; input: unknown }\n > = [];\n\n if (message.content && message.content.trim()) {\n contentBlocks.push({\n type: \"text\",\n text: message.content,\n });\n }\n\n for (const toolCall of message.toolCalls ?? []) {\n contentBlocks.push({\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input,\n });\n }\n return contentBlocks;\n }\n\n private buildToolChoice(\n toolChoice: LLMToolCallOptions[\"toolChoice\"],\n ): VibeProxyMessagesRequest[\"tool_choice\"] {\n if (toolChoice === \"auto\") {\n return { type: \"auto\" };\n }\n if (toolChoice === \"required\") {\n return { type: \"any\" };\n }\n if (toolChoice && typeof toolChoice === \"object\") {\n return { type: \"tool\", name: toolChoice.function.name };\n }\n return undefined;\n }\n\n private extractToolCalls(response: VibeProxyMessagesResponse): LLMToolCall[] {\n return response.content\n .filter(\n (\n c,\n ): c is {\n type: \"tool_use\";\n id: string;\n name: string;\n input: unknown;\n } => c.type === \"tool_use\",\n )\n .map((tc) => ({\n id: tc.id,\n name: tc.name,\n input: tc.input,\n }));\n }\n\n async complete(prompt: string, options?: LLMOptions): Promise<LLMResponse> {\n const model = options?.model ?? this.defaultModel;\n\n const requestBody: VibeProxyMessagesRequest = {\n model,\n max_tokens: options?.maxTokens ?? this.defaultMaxTokens,\n messages: [{ role: \"user\", content: prompt }],\n };\n\n if (options?.systemPrompt) {\n requestBody.system = options.systemPrompt;\n }\n if (options?.temperature !== undefined) {\n requestBody.temperature = options.temperature;\n }\n if (options?.stop) {\n requestBody.stop_sequences = options.stop;\n }\n\n const response = await this.request<VibeProxyMessagesResponse>(\n \"/v1/messages\",\n requestBody,\n {\n ...this.buildExecutionHeaders(options?.execution),\n ...this.buildTraceHeaders(options),\n },\n );\n\n return {\n content: this.extractTextContent(response),\n usage: {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,\n cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,\n providerUsage: {\n stopReason: response.stop_reason,\n },\n },\n model: response.model,\n };\n }\n\n async *stream(prompt: string, options?: LLMOptions): AsyncIterable<string> {\n // VibeProxy supports streaming via SSE, but for simplicity we'll use non-streaming\n // and yield the full response. Can be enhanced later with proper SSE support.\n const response = await this.complete(prompt, options);\n yield response.content;\n }\n\n /**\n * Chat with native tool calling support.\n */\n async chatWithTools(\n messages: LLMMessage[],\n options: LLMToolCallOptions,\n ): Promise<LLMToolCallResponse> {\n const model = options?.model ?? this.defaultModel;\n const converted = this.convertMessagesForTools(messages);\n\n // Convert LLMTool[] to VibeProxy tools format\n const tools: VibeProxyMessagesRequest[\"tools\"] = options.tools.map(\n (tool: LLMTool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: tool.inputSchema,\n }),\n );\n const toolChoice = this.buildToolChoice(options.toolChoice);\n\n const requestBody: VibeProxyMessagesRequest = {\n model,\n max_tokens: options?.maxTokens ?? this.defaultMaxTokens,\n messages: converted.messages,\n tools: options.toolChoice !== \"none\" ? tools : undefined,\n tool_choice: options.toolChoice !== \"none\" ? toolChoice : undefined,\n };\n\n if (converted.systemPrompt || options?.systemPrompt) {\n requestBody.system = converted.systemPrompt ?? options.systemPrompt;\n }\n if (options?.temperature !== undefined) {\n requestBody.temperature = options.temperature;\n }\n if (options?.stop) {\n requestBody.stop_sequences = options.stop;\n }\n\n const response = await this.request<VibeProxyMessagesResponse>(\n \"/v1/messages\",\n requestBody,\n {\n ...this.buildExecutionHeaders(options?.execution),\n ...this.buildTraceHeaders(options),\n },\n );\n const textContent = this.extractTextContent(response);\n const toolCalls = this.extractToolCalls(response);\n\n return {\n content: textContent,\n usage: {\n promptTokens: response.usage.input_tokens,\n completionTokens: response.usage.output_tokens,\n cacheReadTokens: response.usage.cache_read_input_tokens ?? 0,\n cacheWriteTokens: response.usage.cache_creation_input_tokens ?? 0,\n providerUsage: {\n stopReason: response.stop_reason,\n },\n },\n model: response.model,\n toolCalls: toolCalls.length > 0 ? toolCalls : undefined,\n stopReason: response.stop_reason,\n };\n }\n}\n\n/**\n * Create VibeProxy LLM adapter.\n * This is the factory function called by initPlatform() when loading adapters.\n */\nexport function createAdapter(config?: VibeProxyLLMConfig): VibeProxyLLM {\n return new VibeProxyLLM(config);\n}\n\n// Default export for direct import\nexport default createAdapter;\n"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@kb-labs/adapters-vibeproxy",
3
+ "version": "0.2.0",
4
+ "description": "VibeProxy adapter implementing ILLM interface for local Anthropic API proxy",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./llm": {
14
+ "import": "./dist/llm.js",
15
+ "types": "./dist/llm.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "sideEffects": false,
23
+ "scripts": {
24
+ "clean": "rimraf dist",
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "type-check": "tsc --noEmit",
28
+ "test": "vitest run --passWithNoTests",
29
+ "test:watch": "vitest",
30
+ "lint": "eslint src --ext .ts",
31
+ "lint:fix": "eslint . --fix"
32
+ },
33
+ "dependencies": {},
34
+ "peerDependencies": {
35
+ "@kb-labs/core-platform": "*"
36
+ },
37
+ "devDependencies": {
38
+ "@kb-labs/core-platform": "link:../../../../platform/kb-labs-core/packages/core-platform",
39
+ "@types/node": "^24.3.3",
40
+ "eslint": "^9",
41
+ "rimraf": "^6.0.1",
42
+ "tsup": "^8.5.0",
43
+ "typescript": "^5.6.3",
44
+ "vitest": "^3.2.4",
45
+ "@kb-labs/devkit": "link:../../../kb-labs-devkit"
46
+ },
47
+ "engines": {
48
+ "node": ">=20.0.0",
49
+ "pnpm": ">=9.0.0"
50
+ }
51
+ }