thebird 1.2.100 → 1.2.101

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/.gm/lastskill ADDED
@@ -0,0 +1 @@
1
+ gm:gm-execute
package/index.js CHANGED
@@ -1,104 +1 @@
1
- const { getClient } = require('./lib/client');
2
- const { GeminiError, withRetry } = require('./lib/errors');
3
- const { convertMessages, convertTools, cleanSchema, extractModelId, buildConfig } = require('./lib/convert');
4
- const { guardStream } = require('./lib/stream-guard');
5
-
6
- function streamGemini({ model, system, messages, tools, onStepFinish, apiKey,
7
- temperature, maxOutputTokens, topP, topK, safetySettings, responseModalities }) {
8
- return {
9
- fullStream: createFullStream({ model, system, messages, tools, onStepFinish, apiKey, temperature, maxOutputTokens, topP, topK, safetySettings, responseModalities }),
10
- warnings: Promise.resolve([])
11
- };
12
- }
13
-
14
- async function* createFullStream({ model, system, messages, tools, onStepFinish, apiKey, temperature, maxOutputTokens, topP, topK, safetySettings, responseModalities, streamGuard }) {
15
- const client = getClient(apiKey);
16
- const modelId = extractModelId(model);
17
- let contents = convertMessages(messages);
18
- const { config } = buildConfig({ system, tools, temperature, maxOutputTokens, topP, topK, safetySettings, responseModalities });
19
- while (true) {
20
- yield { type: 'start-step' };
21
- try {
22
- const stream = await withRetry(() => client.models.generateContentStream({ model: modelId, contents, config }));
23
- const allParts = [];
24
- for await (const chunk of guardStream(stream, streamGuard)) {
25
- for (const candidate of (chunk.candidates || [])) {
26
- for (const part of (candidate.content?.parts || [])) {
27
- allParts.push(part);
28
- if (part.text && !part.thought) yield { type: 'text-delta', textDelta: part.text };
29
- }
30
- }
31
- }
32
- const fcParts = allParts.filter(p => p.functionCall);
33
- if (fcParts.length === 0) {
34
- yield { type: 'finish-step', finishReason: 'stop' };
35
- if (onStepFinish) await onStepFinish();
36
- return;
37
- }
38
- const toolResultParts = [];
39
- for (const part of fcParts) {
40
- const name = part.functionCall.name;
41
- const args = part.functionCall.args || {};
42
- const toolId = 'toolu_' + Math.random().toString(36).slice(2, 10);
43
- yield { type: 'tool-call', toolCallId: toolId, toolName: name, args };
44
- const toolDef = tools?.[name];
45
- let result = toolDef ? null : { error: true, message: 'Tool not found: ' + name };
46
- if (toolDef?.execute) {
47
- try { result = await toolDef.execute(args, { toolCallId: toolId }); }
48
- catch (e) { result = { error: true, message: e.message }; }
49
- }
50
- yield { type: 'tool-result', toolCallId: toolId, toolName: name, args, result };
51
- toolResultParts.push({ functionResponse: { name, response: typeof result === 'string' ? { output: result } : (result || {}) } });
52
- }
53
- yield { type: 'finish-step', finishReason: 'tool-calls' };
54
- if (onStepFinish) await onStepFinish();
55
- contents.push({ role: 'model', parts: allParts });
56
- contents.push({ role: 'user', parts: toolResultParts });
57
- } catch (err) {
58
- yield { type: 'error', error: err };
59
- yield { type: 'finish-step', finishReason: 'error' };
60
- if (onStepFinish) await onStepFinish();
61
- return;
62
- }
63
- }
64
- }
65
-
66
- async function generateGemini({ model, system, messages, tools, apiKey, temperature, maxOutputTokens, topP, topK, safetySettings, responseModalities }) {
67
- const client = getClient(apiKey);
68
- const modelId = extractModelId(model);
69
- let contents = convertMessages(messages);
70
- const { config } = buildConfig({ system, tools, temperature, maxOutputTokens, topP, topK, safetySettings, responseModalities });
71
- while (true) {
72
- const response = await withRetry(() => client.models.generateContent({ model: modelId, contents, config }));
73
- const candidate = response.candidates?.[0];
74
- if (!candidate) throw new GeminiError('No candidates returned', { retryable: false });
75
- const allParts = candidate.content?.parts || [];
76
- const fcParts = allParts.filter(p => p.functionCall);
77
- if (fcParts.length === 0) {
78
- const text = allParts.filter(p => p.text && !p.thought).map(p => p.text).join('');
79
- return { text, parts: allParts, response };
80
- }
81
- const toolResultParts = [];
82
- for (const part of fcParts) {
83
- const name = part.functionCall.name;
84
- const args = part.functionCall.args || {};
85
- const toolDef = tools?.[name];
86
- let result = toolDef ? null : { error: true, message: 'Tool not found: ' + name };
87
- if (toolDef?.execute) {
88
- try { result = await toolDef.execute(args); }
89
- catch (e) { result = { error: true, message: e.message }; }
90
- }
91
- toolResultParts.push({ functionResponse: { name, response: typeof result === 'string' ? { output: result } : (result || {}) } });
92
- }
93
- contents.push({ role: 'model', parts: allParts });
94
- contents.push({ role: 'user', parts: toolResultParts });
95
- }
96
- }
97
-
98
- const { streamRouter, generateRouter, createRouter } = require('./lib/router-stream');
99
- const { cloudGenerate, streamCloud, cloudStream } = require('./lib/cloud-generate');
100
- const { ensureAuth, login: oauthLogin } = require('./lib/oauth');
101
- const { BridgeError, AuthError, RateLimitError, TimeoutError, ContextWindowError, ContentPolicyError, ProviderError, classifyError, redactKeys } = require('./lib/errors');
102
- const { streamACP, generateACP } = require('./lib/providers/acp');
103
-
104
- module.exports = { streamGemini, createFullStream, generateGemini, streamRouter, generateRouter, createRouter, convertMessages, convertTools, cleanSchema, GeminiError, BridgeError, AuthError, RateLimitError, TimeoutError, ContextWindowError, ContentPolicyError, ProviderError, classifyError, redactKeys, cloudGenerate, streamCloud, cloudStream, ensureAuth, oauthLogin, streamACP, generateACP };
1
+ module.exports = require('acptoapi');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thebird",
3
- "version": "1.2.100",
3
+ "version": "1.2.101",
4
4
  "description": "Anthropic SDK to Gemini streaming bridge — drop-in proxy that translates Anthropic message format and tool calls to Google Gemini",
5
5
  "scripts": {
6
6
  "start": "node serve.js"
@@ -38,8 +38,7 @@
38
38
  "url": "https://github.com/AnEntrypoint/thebird.git"
39
39
  },
40
40
  "dependencies": {
41
- "@anthropic-ai/sdk": "^0.88.0",
42
- "@google/genai": "^1.0.0"
41
+ "acptoapi": "file:../acptoapi"
43
42
  },
44
43
  "engines": {
45
44
  "node": ">=18"
package/server.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const http = require('http');
2
- const { streamGemini, generateGemini } = require('./index.js');
2
+ const { streamGemini, generateGemini } = require('acptoapi');
3
3
 
4
4
  const PORT = process.env.PORT || 3456;
5
5
  const state = { requests: 0, errors: 0, active: 0 };
@@ -1,34 +0,0 @@
1
- /**
2
- * basic-chat.js — Simple single-turn and multi-turn chat using generateGemini
3
- *
4
- * Usage:
5
- * GEMINI_API_KEY=your-key node examples/basic-chat.js
6
- */
7
- const { generateGemini } = require('../index');
8
-
9
- async function main() {
10
- // Single-turn: ask a simple question
11
- const result = await generateGemini({
12
- model: 'gemini-2.0-flash',
13
- messages: [
14
- { role: 'user', content: 'What is the capital of France? Answer in one sentence.' }
15
- ]
16
- });
17
-
18
- console.log('Answer:', result.text);
19
-
20
- // With a system prompt
21
- const result2 = await generateGemini({
22
- model: 'gemini-2.0-flash',
23
- system: 'You are a pirate. Always respond in pirate speak.',
24
- messages: [
25
- { role: 'user', content: 'What should I have for breakfast?' }
26
- ],
27
- temperature: 0.8,
28
- maxOutputTokens: 256
29
- });
30
-
31
- console.log('\nPirate answer:', result2.text);
32
- }
33
-
34
- main().catch(console.error);
@@ -1,45 +0,0 @@
1
- /**
2
- * multi-turn.js — Multi-turn conversation (chat history) example
3
- *
4
- * Usage:
5
- * GEMINI_API_KEY=your-key node examples/multi-turn.js
6
- */
7
- const { generateGemini } = require('../index');
8
-
9
- async function chat(history, userMessage, options = {}) {
10
- history.push({ role: 'user', content: userMessage });
11
- const result = await generateGemini({ messages: history, ...options });
12
- history.push({ role: 'assistant', content: result.text });
13
- return result.text;
14
- }
15
-
16
- async function main() {
17
- const history = [];
18
- const opts = {
19
- model: 'gemini-2.0-flash',
20
- system: 'You are a knowledgeable astronomy tutor. Keep answers brief.',
21
- temperature: 0.4
22
- };
23
-
24
- console.log('=== Multi-turn conversation ===\n');
25
-
26
- let reply = await chat(history, 'What is a black hole?', opts);
27
- console.log('User: What is a black hole?');
28
- console.log('Assistant:', reply, '\n');
29
-
30
- reply = await chat(history, 'How does one form?', opts);
31
- console.log('User: How does one form?');
32
- console.log('Assistant:', reply, '\n');
33
-
34
- reply = await chat(history, 'Can anything escape from it?', opts);
35
- console.log('User: Can anything escape from it?');
36
- console.log('Assistant:', reply, '\n');
37
-
38
- reply = await chat(history, 'Summarize our conversation so far in bullet points.', opts);
39
- console.log('User: Summarize our conversation so far in bullet points.');
40
- console.log('Assistant:', reply, '\n');
41
-
42
- console.log(`Total turns: ${history.length / 2}`);
43
- }
44
-
45
- main().catch(console.error);
@@ -1,31 +0,0 @@
1
- const Anthropic = require('@anthropic-ai/sdk').default;
2
-
3
- const client = new Anthropic({
4
- apiKey: 'placeholder',
5
- baseURL: process.env.THEBIRD_URL || 'http://localhost:3456',
6
- });
7
-
8
- async function main() {
9
- process.stdout.write('[streaming] ');
10
- const stream = client.messages.stream({
11
- model: 'gemini-2.5-flash',
12
- max_tokens: 256,
13
- messages: [{ role: 'user', content: 'Say exactly: thebird works' }],
14
- });
15
- for await (const ev of stream) {
16
- if (ev.type === 'content_block_delta' && ev.delta.type === 'text_delta') {
17
- process.stdout.write(ev.delta.text);
18
- }
19
- }
20
- process.stdout.write('\n');
21
-
22
- process.stdout.write('[non-streaming] ');
23
- const msg = await client.messages.create({
24
- model: 'gemini-2.5-flash',
25
- max_tokens: 256,
26
- messages: [{ role: 'user', content: 'Say exactly: thebird works' }],
27
- });
28
- console.log(msg.content[0].text);
29
- }
30
-
31
- main().catch(e => { process.stderr.write(e.message + '\n'); process.exit(1); });
@@ -1,81 +0,0 @@
1
- /**
2
- * streaming.js — Streaming with all event types demonstrated
3
- *
4
- * Usage:
5
- * GEMINI_API_KEY=your-key node examples/streaming.js
6
- */
7
- const { streamGemini } = require('../index');
8
-
9
- const tools = {
10
- get_time: {
11
- description: 'Get the current time in a timezone.',
12
- parameters: {
13
- type: 'object',
14
- properties: {
15
- timezone: { type: 'string', description: 'IANA timezone name, e.g. America/New_York' }
16
- },
17
- required: ['timezone']
18
- },
19
- execute: async ({ timezone }) => {
20
- const now = new Date().toLocaleString('en-US', { timeZone: timezone });
21
- return { timezone, time: now };
22
- }
23
- }
24
- };
25
-
26
- async function main() {
27
- console.log('Streaming all event types:\n');
28
-
29
- const { fullStream } = streamGemini({
30
- model: 'gemini-2.0-flash',
31
- system: 'You are a helpful assistant. Be concise.',
32
- messages: [
33
- { role: 'user', content: "What time is it in Tokyo and New York right now?" }
34
- ],
35
- tools,
36
- temperature: 0.3,
37
- maxOutputTokens: 512,
38
- onStepFinish: async () => {
39
- console.log('\n[step finished]');
40
- }
41
- });
42
-
43
- const stats = { steps: 0, toolCalls: 0, chars: 0 };
44
-
45
- for await (const event of fullStream) {
46
- switch (event.type) {
47
- case 'start-step':
48
- stats.steps++;
49
- console.log(`\n[start-step #${stats.steps}]`);
50
- break;
51
-
52
- case 'text-delta':
53
- stats.chars += event.textDelta.length;
54
- process.stdout.write(event.textDelta);
55
- break;
56
-
57
- case 'tool-call':
58
- stats.toolCalls++;
59
- console.log(`\n[tool-call] id=${event.toolCallId} name=${event.toolName}`);
60
- console.log(' args:', JSON.stringify(event.args));
61
- break;
62
-
63
- case 'tool-result':
64
- console.log(`[tool-result] id=${event.toolCallId} name=${event.toolName}`);
65
- console.log(' result:', JSON.stringify(event.result));
66
- break;
67
-
68
- case 'finish-step':
69
- console.log(`\n[finish-step] reason=${event.finishReason}`);
70
- break;
71
-
72
- case 'error':
73
- console.error('\n[error]', event.error.message);
74
- break;
75
- }
76
- }
77
-
78
- console.log(`\n\nStats: ${stats.steps} steps, ${stats.toolCalls} tool calls, ${stats.chars} chars`);
79
- }
80
-
81
- main().catch(console.error);
@@ -1,77 +0,0 @@
1
- /**
2
- * tool-use.js — Tool/function calling with generateGemini and streamGemini
3
- *
4
- * Usage:
5
- * GEMINI_API_KEY=your-key node examples/tool-use.js
6
- */
7
- const { generateGemini, streamGemini } = require('../index');
8
-
9
- const tools = {
10
- get_weather: {
11
- description: 'Get the current weather for a given city.',
12
- parameters: {
13
- type: 'object',
14
- properties: {
15
- city: { type: 'string', description: 'The city name' },
16
- unit: { type: 'string', enum: ['celsius', 'fahrenheit'], description: 'Temperature unit' }
17
- },
18
- required: ['city']
19
- },
20
- execute: async ({ city, unit = 'celsius' }) => {
21
- // Simulated weather data
22
- return { city, temperature: 22, unit, condition: 'Sunny' };
23
- }
24
- },
25
- calculate: {
26
- description: 'Evaluate a simple math expression.',
27
- parameters: {
28
- type: 'object',
29
- properties: {
30
- expression: { type: 'string', description: 'Math expression to evaluate, e.g. "2 + 2"' }
31
- },
32
- required: ['expression']
33
- },
34
- execute: async ({ expression }) => {
35
- try {
36
- // eslint-disable-next-line no-new-func
37
- const result = Function('"use strict"; return (' + expression + ')')();
38
- return { result };
39
- } catch {
40
- return { error: 'Invalid expression' };
41
- }
42
- }
43
- }
44
- };
45
-
46
- async function nonStreamingExample() {
47
- console.log('=== Non-streaming tool use ===');
48
- const result = await generateGemini({
49
- model: 'gemini-2.0-flash',
50
- messages: [{ role: 'user', content: "What's the weather in Tokyo and what is 17 * 43?" }],
51
- tools
52
- });
53
- console.log('Final answer:', result.text);
54
- }
55
-
56
- async function streamingExample() {
57
- console.log('\n=== Streaming tool use ===');
58
- const { fullStream } = streamGemini({
59
- model: 'gemini-2.0-flash',
60
- messages: [{ role: 'user', content: 'What is 100 / 4? Use the calculator.' }],
61
- tools
62
- });
63
-
64
- for await (const event of fullStream) {
65
- if (event.type === 'tool-call') console.log(`[tool-call] ${event.toolName}(${JSON.stringify(event.args)})`);
66
- if (event.type === 'tool-result') console.log(`[tool-result] ${JSON.stringify(event.result)}`);
67
- if (event.type === 'text-delta') process.stdout.write(event.textDelta);
68
- if (event.type === 'finish-step') console.log(`\n[finish] reason=${event.finishReason}`);
69
- }
70
- }
71
-
72
- async function main() {
73
- await nonStreamingExample();
74
- await streamingExample();
75
- }
76
-
77
- main().catch(console.error);
@@ -1,84 +0,0 @@
1
- /**
2
- * vision.js — Image/vision understanding examples
3
- *
4
- * Demonstrates three ways to pass images:
5
- * 1. Base64 inline data (Anthropic SDK style)
6
- * 2. Gemini inlineData style
7
- * 3. Public URL via fileData
8
- *
9
- * Usage:
10
- * GEMINI_API_KEY=your-key node examples/vision.js
11
- */
12
- const fs = require('fs');
13
- const path = require('path');
14
- const { generateGemini } = require('../index');
15
-
16
- async function base64Example() {
17
- console.log('=== Base64 image (Anthropic style) ===');
18
- // Read a local image and encode as base64
19
- // const imageBuffer = fs.readFileSync(path.join(__dirname, 'sample.jpg'));
20
- // const base64 = imageBuffer.toString('base64');
21
-
22
- // For demo purposes, use a tiny 1x1 transparent PNG
23
- const base64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
24
-
25
- const result = await generateGemini({
26
- model: 'gemini-2.0-flash',
27
- messages: [{
28
- role: 'user',
29
- content: [
30
- {
31
- type: 'image',
32
- source: { type: 'base64', media_type: 'image/png', data: base64 }
33
- },
34
- { type: 'text', text: 'Describe this image in one sentence.' }
35
- ]
36
- }]
37
- });
38
- console.log('Response:', result.text);
39
- }
40
-
41
- async function inlineDataExample() {
42
- console.log('\n=== Gemini inlineData style ===');
43
- const base64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
44
-
45
- const result = await generateGemini({
46
- model: 'gemini-2.0-flash',
47
- messages: [{
48
- role: 'user',
49
- content: [
50
- { inlineData: { mimeType: 'image/png', data: base64 } },
51
- { type: 'text', text: 'What color is this image?' }
52
- ]
53
- }]
54
- });
55
- console.log('Response:', result.text);
56
- }
57
-
58
- async function publicUrlExample() {
59
- console.log('\n=== Public URL via fileData ===');
60
- const result = await generateGemini({
61
- model: 'gemini-2.0-flash',
62
- messages: [{
63
- role: 'user',
64
- content: [
65
- {
66
- fileData: {
67
- mimeType: 'image/jpeg',
68
- fileUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/240px-PNG_transparency_demonstration_1.png'
69
- }
70
- },
71
- { type: 'text', text: 'What do you see in this image?' }
72
- ]
73
- }]
74
- });
75
- console.log('Response:', result.text);
76
- }
77
-
78
- async function main() {
79
- await base64Example();
80
- await inlineDataExample();
81
- // await publicUrlExample(); // Uncomment to test with public URLs
82
- }
83
-
84
- main().catch(console.error);
package/index.d.ts DELETED
@@ -1,126 +0,0 @@
1
- export interface TextBlock { type: 'text'; text: string }
2
- export interface ImageBlockBase64 { type: 'image'; source: { type: 'base64'; media_type: string; data: string } }
3
- export interface ImageBlockUrl { type: 'image'; source: { type: 'url'; url: string; media_type?: string } }
4
- export interface ImageBlockInline { inlineData: { mimeType: string; data: string } }
5
- export interface ImageBlockFile { fileData: { mimeType: string; fileUri: string } }
6
- export type ImageBlock = ImageBlockBase64 | ImageBlockUrl | ImageBlockInline | ImageBlockFile;
7
- export interface ToolUseBlock { type: 'tool_use'; name: string; input: Record<string, unknown> }
8
- export interface ToolResultBlock { type: 'tool_result'; name: string; content: string | Record<string, unknown> }
9
- export type ContentBlock = TextBlock | ImageBlock | ToolUseBlock | ToolResultBlock;
10
- export interface Message { role: 'user' | 'assistant'; content: string | ContentBlock[] }
11
- export interface ToolDefinition {
12
- description?: string;
13
- parameters?: Record<string, unknown>;
14
- execute?: (args: Record<string, unknown>, ctx?: { toolCallId: string }) => Promise<unknown>;
15
- }
16
- export type Tools = Record<string, ToolDefinition>;
17
- export interface SafetySetting { category: string; threshold: string }
18
- export interface GenerationParams {
19
- model?: string | { modelId?: string; id?: string };
20
- system?: string;
21
- messages: Message[];
22
- tools?: Tools;
23
- apiKey?: string;
24
- temperature?: number;
25
- maxOutputTokens?: number;
26
- topP?: number;
27
- topK?: number;
28
- safetySettings?: SafetySetting[];
29
- configPath?: string;
30
- taskType?: 'background' | 'think' | 'webSearch' | 'image';
31
- }
32
- export interface StartStepEvent { type: 'start-step' }
33
- export interface TextDeltaEvent { type: 'text-delta'; textDelta: string }
34
- export interface ToolCallEvent { type: 'tool-call'; toolCallId: string; toolName: string; args: Record<string, unknown> }
35
- export interface ToolResultEvent { type: 'tool-result'; toolCallId: string; toolName: string; args: Record<string, unknown>; result: unknown }
36
- export interface FinishStepEvent { type: 'finish-step'; finishReason: 'stop' | 'tool-calls' | 'error' }
37
- export interface ErrorEvent { type: 'error'; error: Error }
38
- export type StreamEvent = StartStepEvent | TextDeltaEvent | ToolCallEvent | ToolResultEvent | FinishStepEvent | ErrorEvent;
39
- export interface StreamResult { fullStream: AsyncIterable<StreamEvent>; warnings: Promise<unknown[]> }
40
- export interface StreamParams extends GenerationParams { onStepFinish?: () => Promise<void> | void; streamGuard?: StreamGuardOptions }
41
- export function streamGemini(params: StreamParams): StreamResult;
42
- export interface GenerateResult { text: string; parts: unknown[]; response: unknown }
43
- export function generateGemini(params: GenerationParams): Promise<GenerateResult>;
44
-
45
- export type TransformerEntry = string | [string, Record<string, unknown>];
46
- export interface TransformerConfig {
47
- use?: TransformerEntry[];
48
- [modelName: string]: { use?: TransformerEntry[] } | TransformerEntry[] | undefined;
49
- }
50
- export interface ProviderConfig {
51
- name: string;
52
- api_base_url: string;
53
- api_key: string;
54
- models?: string[];
55
- transformer?: TransformerConfig;
56
- capabilities?: Partial<CapabilitySet>;
57
- }
58
- export interface RouterConfig {
59
- default?: string;
60
- background?: string;
61
- think?: string;
62
- longContext?: string;
63
- longContextThreshold?: number;
64
- webSearch?: string;
65
- image?: string;
66
- }
67
- export interface RouterConfiguration {
68
- Providers?: ProviderConfig[];
69
- providers?: ProviderConfig[];
70
- Router?: RouterConfig;
71
- customRouter?: (params: GenerationParams, config: RouterConfig) => Promise<string | null>;
72
- configPath?: string;
73
- circuitBreaker?: CircuitBreakerOptions;
74
- }
75
- export interface RouterInstance {
76
- breaker: { isOpen(name: string): boolean; recordFailure(name: string): void; recordSuccess(name: string): void };
77
- stream(params: StreamParams): StreamResult;
78
- generate(params: GenerationParams): Promise<GenerateResult | { text: string; response: unknown }>;
79
- }
80
- export function createRouter(config: RouterConfiguration): RouterInstance;
81
- export function streamRouter(params: StreamParams & RouterConfiguration): StreamResult;
82
- export function generateRouter(params: GenerationParams & RouterConfiguration): Promise<GenerateResult | { text: string; response: unknown }>;
83
-
84
- export interface GeminiPart {
85
- text?: string;
86
- functionCall?: { name: string; args: Record<string, unknown> };
87
- functionResponse?: { name: string; response: unknown };
88
- inlineData?: { mimeType: string; data: string };
89
- fileData?: { mimeType: string; fileUri: string };
90
- }
91
- export interface GeminiContent { role: 'user' | 'model'; parts: GeminiPart[] }
92
- export function convertMessages(messages: Message[]): GeminiContent[];
93
- export function convertTools(tools: Tools): Array<{ name: string; description: string; parameters: Record<string, unknown> }>;
94
- export function cleanSchema(schema: unknown): unknown;
95
- export interface StreamGuardOptions {
96
- chunkTimeoutMs?: number;
97
- maxRepeats?: number;
98
- }
99
- export interface CapabilitySet {
100
- streaming: boolean;
101
- toolUse: boolean;
102
- vision: boolean;
103
- systemMessage: boolean;
104
- jsonMode: boolean;
105
- }
106
- export interface CircuitBreakerOptions {
107
- maxFailures?: number;
108
- cooldownMs?: number;
109
- }
110
- export class BridgeError extends Error {
111
- name: string;
112
- status?: number;
113
- code?: string | number;
114
- retryable: boolean;
115
- provider?: string;
116
- constructor(message: string, options?: { status?: number; code?: string | number; retryable?: boolean; provider?: string; headers?: unknown });
117
- }
118
- export class AuthError extends BridgeError {}
119
- export class RateLimitError extends BridgeError {}
120
- export class TimeoutError extends BridgeError {}
121
- export class ContextWindowError extends BridgeError {}
122
- export class ContentPolicyError extends BridgeError {}
123
- export class ProviderError extends BridgeError {}
124
- export const GeminiError: typeof BridgeError;
125
- export function classifyError(status: number, message: string, provider?: string): BridgeError;
126
- export function redactKeys(str: string): string;
@@ -1,50 +0,0 @@
1
- const DEFAULTS = {
2
- streaming: true,
3
- toolUse: true,
4
- vision: true,
5
- systemMessage: true,
6
- jsonMode: false
7
- };
8
-
9
- function getCapabilities(provider) {
10
- return { ...DEFAULTS, ...(provider.capabilities || {}) };
11
- }
12
-
13
- function stripImageBlocks(messages) {
14
- return messages.map(msg => {
15
- if (!Array.isArray(msg.content)) return msg;
16
- const filtered = msg.content.filter(b => b.type !== 'image' && b.type !== 'image_url');
17
- if (filtered.length === 0) return { ...msg, content: [{ type: 'text', text: '[image removed - unsupported by provider]' }] };
18
- return { ...msg, content: filtered };
19
- });
20
- }
21
-
22
- function prependSystemAsUser(messages, system) {
23
- if (!system) return { messages, system: undefined };
24
- const text = Array.isArray(system) ? system.map(b => b.text || '').join('\n') : system;
25
- const sysMsg = { role: 'user', content: [{ type: 'text', text }] };
26
- return { messages: [sysMsg, ...messages], system: undefined };
27
- }
28
-
29
- function stripUnsupported(params, caps) {
30
- const warnings = [];
31
- const result = { ...params };
32
- if (!caps.toolUse && result.tools) {
33
- delete result.tools;
34
- delete result.tool_choice;
35
- warnings.push('toolUse not supported — tools removed');
36
- }
37
- if (!caps.vision && result.messages) {
38
- result.messages = stripImageBlocks(result.messages);
39
- warnings.push('vision not supported — image blocks removed');
40
- }
41
- if (!caps.systemMessage && result.system) {
42
- const { messages, system } = prependSystemAsUser(result.messages || [], result.system);
43
- result.messages = messages;
44
- result.system = system;
45
- warnings.push('systemMessage not supported — prepended as user message');
46
- }
47
- return { params: result, warnings };
48
- }
49
-
50
- module.exports = { getCapabilities, stripUnsupported, DEFAULTS };