@compilr-dev/agents 0.3.2 → 0.3.4
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/dist/agent.d.ts +28 -1
- package/dist/agent.js +12 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/providers/claude.js +7 -0
- package/dist/providers/fireworks.d.ts +86 -0
- package/dist/providers/fireworks.js +123 -0
- package/dist/providers/gemini-native.js +61 -26
- package/dist/providers/groq.d.ts +86 -0
- package/dist/providers/groq.js +123 -0
- package/dist/providers/index.d.ts +10 -0
- package/dist/providers/index.js +6 -0
- package/dist/providers/openai-compatible.js +12 -1
- package/dist/providers/openrouter.d.ts +95 -0
- package/dist/providers/openrouter.js +138 -0
- package/dist/providers/perplexity.d.ts +86 -0
- package/dist/providers/perplexity.js +123 -0
- package/dist/providers/together.d.ts +86 -0
- package/dist/providers/together.js +123 -0
- package/dist/providers/types.d.ts +8 -0
- package/dist/tools/builtin/ask-user-simple.js +1 -0
- package/dist/tools/builtin/ask-user.js +1 -0
- package/dist/tools/builtin/bash.js +123 -2
- package/dist/tools/builtin/shell-manager.d.ts +15 -0
- package/dist/tools/builtin/shell-manager.js +51 -0
- package/dist/tools/builtin/suggest.js +1 -0
- package/dist/tools/builtin/todo.js +2 -0
- package/dist/tools/define.d.ts +6 -0
- package/dist/tools/define.js +1 -0
- package/dist/tools/types.d.ts +19 -0
- package/package.json +4 -1
package/dist/agent.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Agent - The main class for running AI agents with tool use
|
|
3
3
|
*/
|
|
4
4
|
import type { LLMProvider, Message, ChatOptions, StreamChunk } from './providers/types.js';
|
|
5
|
-
import type { Tool, ToolDefinition, ToolRegistry, ToolExecutionResult } from './tools/types.js';
|
|
5
|
+
import type { Tool, ToolDefinition, ToolRegistry, ToolExecutionResult, ToolExecutionContext } from './tools/types.js';
|
|
6
6
|
import type { ContextStats, VerbosityLevel, SmartCompactionResult } from './context/types.js';
|
|
7
7
|
import type { AgentState, Checkpointer, SessionMetadata } from './state/types.js';
|
|
8
8
|
import type { Anchor, AnchorInput, AnchorQueryOptions, AnchorClearOptions, AnchorManagerOptions } from './anchors/types.js';
|
|
@@ -589,6 +589,29 @@ export interface RunOptions {
|
|
|
589
589
|
* ```
|
|
590
590
|
*/
|
|
591
591
|
toolFilter?: string[];
|
|
592
|
+
/**
|
|
593
|
+
* Callback to provide additional tool execution context.
|
|
594
|
+
* Called before each tool execution, allowing the caller to inject
|
|
595
|
+
* abort signals or other context-specific options.
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```typescript
|
|
599
|
+
* // Provide abort signal for bash commands (for Ctrl+B backgrounding)
|
|
600
|
+
* const bashAbortController = new AbortController();
|
|
601
|
+
* await agent.stream(message, {
|
|
602
|
+
* getToolContext: (toolName, toolUseId) => {
|
|
603
|
+
* if (toolName === 'bash') {
|
|
604
|
+
* return {
|
|
605
|
+
* abortSignal: bashAbortController.signal,
|
|
606
|
+
* onBackground: (shellId, output) => { ... },
|
|
607
|
+
* };
|
|
608
|
+
* }
|
|
609
|
+
* return {};
|
|
610
|
+
* },
|
|
611
|
+
* });
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
getToolContext?: (toolName: string, toolUseId: string) => Partial<Omit<ToolExecutionContext, 'toolUseId' | 'onOutput'>>;
|
|
592
615
|
}
|
|
593
616
|
/**
|
|
594
617
|
* Agent run result
|
|
@@ -1505,6 +1528,10 @@ export declare class Agent {
|
|
|
1505
1528
|
* Get all registered tool definitions
|
|
1506
1529
|
*/
|
|
1507
1530
|
getToolDefinitions(): ToolDefinition[];
|
|
1531
|
+
/**
|
|
1532
|
+
* Check if a tool is marked as silent (no spinner or result output)
|
|
1533
|
+
*/
|
|
1534
|
+
isToolSilent(name: string): boolean;
|
|
1508
1535
|
/**
|
|
1509
1536
|
* Run the agent with a user message
|
|
1510
1537
|
*/
|
package/dist/agent.js
CHANGED
|
@@ -1422,6 +1422,13 @@ export class Agent {
|
|
|
1422
1422
|
getToolDefinitions() {
|
|
1423
1423
|
return this.toolRegistry.getDefinitions();
|
|
1424
1424
|
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Check if a tool is marked as silent (no spinner or result output)
|
|
1427
|
+
*/
|
|
1428
|
+
isToolSilent(name) {
|
|
1429
|
+
const tool = this.toolRegistry.get(name);
|
|
1430
|
+
return tool?.silent === true;
|
|
1431
|
+
}
|
|
1425
1432
|
/**
|
|
1426
1433
|
* Run the agent with a user message
|
|
1427
1434
|
*/
|
|
@@ -1429,6 +1436,7 @@ export class Agent {
|
|
|
1429
1436
|
let maxIterations = options?.maxIterations ?? this.maxIterations;
|
|
1430
1437
|
const chatOptions = { ...this.chatOptions, ...options?.chatOptions };
|
|
1431
1438
|
const signal = options?.signal;
|
|
1439
|
+
const getToolContext = options?.getToolContext;
|
|
1432
1440
|
// Combined event emitter
|
|
1433
1441
|
const emit = (event) => {
|
|
1434
1442
|
this.onEvent?.(event);
|
|
@@ -1817,6 +1825,8 @@ export class Agent {
|
|
|
1817
1825
|
}
|
|
1818
1826
|
const toolStartTime = Date.now();
|
|
1819
1827
|
try {
|
|
1828
|
+
// Get additional context from caller (e.g., for bash backgrounding)
|
|
1829
|
+
const additionalContext = getToolContext?.(toolUse.name, toolUse.id) ?? {};
|
|
1820
1830
|
const toolContext = {
|
|
1821
1831
|
toolUseId: toolUse.id,
|
|
1822
1832
|
onOutput: (output, stream) => {
|
|
@@ -1828,6 +1838,8 @@ export class Agent {
|
|
|
1828
1838
|
stream,
|
|
1829
1839
|
});
|
|
1830
1840
|
},
|
|
1841
|
+
// Merge in additional context (abortSignal, onBackground, etc.)
|
|
1842
|
+
...additionalContext,
|
|
1831
1843
|
};
|
|
1832
1844
|
result = await this.toolRegistry.execute(toolUse.name, toolInput, toolContext);
|
|
1833
1845
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,16 @@ export { GeminiLegacyProvider, createGeminiLegacyProvider } from './providers/in
|
|
|
21
21
|
export type { GeminiLegacyProviderConfig } from './providers/index.js';
|
|
22
22
|
export { OpenAICompatibleProvider } from './providers/index.js';
|
|
23
23
|
export type { OpenAICompatibleConfig } from './providers/index.js';
|
|
24
|
+
export { TogetherProvider, createTogetherProvider } from './providers/index.js';
|
|
25
|
+
export type { TogetherProviderConfig } from './providers/index.js';
|
|
26
|
+
export { GroqProvider, createGroqProvider } from './providers/index.js';
|
|
27
|
+
export type { GroqProviderConfig } from './providers/index.js';
|
|
28
|
+
export { FireworksProvider, createFireworksProvider } from './providers/index.js';
|
|
29
|
+
export type { FireworksProviderConfig } from './providers/index.js';
|
|
30
|
+
export { PerplexityProvider, createPerplexityProvider } from './providers/index.js';
|
|
31
|
+
export type { PerplexityProviderConfig } from './providers/index.js';
|
|
32
|
+
export { OpenRouterProvider, createOpenRouterProvider } from './providers/index.js';
|
|
33
|
+
export type { OpenRouterProviderConfig } from './providers/index.js';
|
|
24
34
|
export type { Tool, ToolHandler, ToolRegistry, ToolInputSchema, ToolExecutionResult, ToolRegistryOptions, DefineToolOptions, ReadFileInput, WriteFileInput, BashInput, BashResult, FifoDetectionResult, GrepInput, GlobInput, EditInput, TodoWriteInput, TodoReadInput, TodoItem, TodoStatus, TodoContextCleanupOptions, TaskInput, TaskResult, AgentTypeConfig, TaskToolOptions, ContextMode, ThoroughnessLevel, SubAgentEventInfo, SuggestInput, SuggestToolOptions, } from './tools/index.js';
|
|
25
35
|
export { defineTool, createSuccessResult, createErrorResult, wrapToolExecute, DefaultToolRegistry, createToolRegistry, } from './tools/index.js';
|
|
26
36
|
export { readFileTool, createReadFileTool, writeFileTool, createWriteFileTool, bashTool, createBashTool, execStream, detectFifoUsage, bashOutputTool, createBashOutputTool, killShellTool, createKillShellTool, ShellManager, getDefaultShellManager, setDefaultShellManager, grepTool, createGrepTool, globTool, createGlobTool, editTool, createEditTool, todoWriteTool, todoReadTool, createTodoTools, TodoStore, resetDefaultTodoStore, getDefaultTodoStore, createIsolatedTodoStore, cleanupTodoContextMessages, getTodoContextStats, webFetchTool, createWebFetchTool, createTaskTool, defaultAgentTypes, suggestTool, createSuggestTool, builtinTools, allBuiltinTools, TOOL_NAMES, TOOL_SETS, } from './tools/index.js';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,12 @@ export { GeminiNativeProvider, createGeminiNativeProvider } from './providers/in
|
|
|
16
16
|
// Legacy Gemini provider (OpenAI-compatible endpoint)
|
|
17
17
|
export { GeminiLegacyProvider, createGeminiLegacyProvider } from './providers/index.js';
|
|
18
18
|
export { OpenAICompatibleProvider } from './providers/index.js';
|
|
19
|
+
// "Others" providers - OpenAI-compatible cloud APIs
|
|
20
|
+
export { TogetherProvider, createTogetherProvider } from './providers/index.js';
|
|
21
|
+
export { GroqProvider, createGroqProvider } from './providers/index.js';
|
|
22
|
+
export { FireworksProvider, createFireworksProvider } from './providers/index.js';
|
|
23
|
+
export { PerplexityProvider, createPerplexityProvider } from './providers/index.js';
|
|
24
|
+
export { OpenRouterProvider, createOpenRouterProvider } from './providers/index.js';
|
|
19
25
|
// Tool utilities
|
|
20
26
|
export { defineTool, createSuccessResult, createErrorResult, wrapToolExecute, DefaultToolRegistry, createToolRegistry, } from './tools/index.js';
|
|
21
27
|
// Built-in tools
|
package/dist/providers/claude.js
CHANGED
|
@@ -43,6 +43,12 @@ export class ClaudeProvider {
|
|
|
43
43
|
const { systemPrompt, anthropicMessages } = this.convertMessages(messages);
|
|
44
44
|
const tools = this.convertTools(options?.tools);
|
|
45
45
|
const thinking = this.convertThinking(options?.thinking);
|
|
46
|
+
// Calculate payload sizes for debugging (same as gemini-native.ts)
|
|
47
|
+
const debugPayload = {
|
|
48
|
+
systemChars: systemPrompt.length,
|
|
49
|
+
contentsChars: JSON.stringify(anthropicMessages).length,
|
|
50
|
+
toolsChars: JSON.stringify(tools).length,
|
|
51
|
+
};
|
|
46
52
|
try {
|
|
47
53
|
// Build request parameters
|
|
48
54
|
const params = {
|
|
@@ -99,6 +105,7 @@ export class ClaudeProvider {
|
|
|
99
105
|
outputTokens: usage.output_tokens,
|
|
100
106
|
cacheReadTokens: usageWithCache.cache_read_input_tokens,
|
|
101
107
|
cacheCreationTokens: usageWithCache.cache_creation_input_tokens,
|
|
108
|
+
debugPayload,
|
|
102
109
|
},
|
|
103
110
|
};
|
|
104
111
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fireworks AI LLM Provider
|
|
3
|
+
*
|
|
4
|
+
* Implements LLMProvider interface for Fireworks AI models.
|
|
5
|
+
* Extends OpenAICompatibleProvider for shared functionality.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const provider = createFireworksProvider({
|
|
10
|
+
* model: 'accounts/fireworks/models/llama-v3p1-70b-instruct',
|
|
11
|
+
* apiKey: process.env.FIREWORKS_API_KEY
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* - Requires valid Fireworks AI API key
|
|
17
|
+
* - Default model is accounts/fireworks/models/llama-v3p1-8b-instruct
|
|
18
|
+
* - Supports Llama, Mixtral, and fine-tuned models
|
|
19
|
+
*/
|
|
20
|
+
import type { ChatOptions } from './types.js';
|
|
21
|
+
import { ProviderError } from '../errors.js';
|
|
22
|
+
import { OpenAICompatibleProvider } from './openai-compatible.js';
|
|
23
|
+
/**
|
|
24
|
+
* Configuration for FireworksProvider
|
|
25
|
+
*/
|
|
26
|
+
export interface FireworksProviderConfig {
|
|
27
|
+
/** Fireworks AI API key (falls back to FIREWORKS_API_KEY env var) */
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
/** Base URL for Fireworks API (default: https://api.fireworks.ai/inference) */
|
|
30
|
+
baseUrl?: string;
|
|
31
|
+
/** Default model to use (default: accounts/fireworks/models/llama-v3p1-8b-instruct) */
|
|
32
|
+
model?: string;
|
|
33
|
+
/** Default max tokens (default: 4096) */
|
|
34
|
+
maxTokens?: number;
|
|
35
|
+
/** Request timeout in milliseconds (default: 120000) */
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fireworks AI LLM Provider
|
|
40
|
+
*
|
|
41
|
+
* Provides streaming chat completion using Fireworks AI.
|
|
42
|
+
* Supports Llama, Mixtral, and custom fine-tuned models.
|
|
43
|
+
*/
|
|
44
|
+
export declare class FireworksProvider extends OpenAICompatibleProvider {
|
|
45
|
+
readonly name = "fireworks";
|
|
46
|
+
private readonly apiKey;
|
|
47
|
+
constructor(config?: FireworksProviderConfig);
|
|
48
|
+
/**
|
|
49
|
+
* Fireworks AI authentication with Bearer token
|
|
50
|
+
*/
|
|
51
|
+
protected getAuthHeaders(): Record<string, string>;
|
|
52
|
+
/**
|
|
53
|
+
* Fireworks AI chat completions endpoint (OpenAI-compatible)
|
|
54
|
+
*/
|
|
55
|
+
protected getEndpointPath(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Fireworks AI uses standard OpenAI body format
|
|
58
|
+
*/
|
|
59
|
+
protected buildProviderSpecificBody(_options?: ChatOptions): Record<string, unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Map HTTP errors with Fireworks AI-specific messages
|
|
62
|
+
*/
|
|
63
|
+
protected mapHttpError(status: number, body: string, _model: string): ProviderError;
|
|
64
|
+
/**
|
|
65
|
+
* Map connection errors with Fireworks AI-specific messages
|
|
66
|
+
*/
|
|
67
|
+
protected mapConnectionError(_error: Error): ProviderError;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a Fireworks AI provider instance
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Using environment variable (FIREWORKS_API_KEY)
|
|
75
|
+
* const provider = createFireworksProvider();
|
|
76
|
+
*
|
|
77
|
+
* // With explicit API key
|
|
78
|
+
* const provider = createFireworksProvider({ apiKey: 'fw_...' });
|
|
79
|
+
*
|
|
80
|
+
* // With custom model
|
|
81
|
+
* const provider = createFireworksProvider({
|
|
82
|
+
* model: 'accounts/fireworks/models/llama-v3p1-70b-instruct'
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function createFireworksProvider(config?: FireworksProviderConfig): FireworksProvider;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fireworks AI LLM Provider
|
|
3
|
+
*
|
|
4
|
+
* Implements LLMProvider interface for Fireworks AI models.
|
|
5
|
+
* Extends OpenAICompatibleProvider for shared functionality.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const provider = createFireworksProvider({
|
|
10
|
+
* model: 'accounts/fireworks/models/llama-v3p1-70b-instruct',
|
|
11
|
+
* apiKey: process.env.FIREWORKS_API_KEY
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* - Requires valid Fireworks AI API key
|
|
17
|
+
* - Default model is accounts/fireworks/models/llama-v3p1-8b-instruct
|
|
18
|
+
* - Supports Llama, Mixtral, and fine-tuned models
|
|
19
|
+
*/
|
|
20
|
+
import { ProviderError } from '../errors.js';
|
|
21
|
+
import { OpenAICompatibleProvider } from './openai-compatible.js';
|
|
22
|
+
// Default configuration
|
|
23
|
+
const DEFAULT_MODEL = 'accounts/fireworks/models/llama-v3p1-8b-instruct';
|
|
24
|
+
const DEFAULT_BASE_URL = 'https://api.fireworks.ai/inference';
|
|
25
|
+
/**
|
|
26
|
+
* Fireworks AI LLM Provider
|
|
27
|
+
*
|
|
28
|
+
* Provides streaming chat completion using Fireworks AI.
|
|
29
|
+
* Supports Llama, Mixtral, and custom fine-tuned models.
|
|
30
|
+
*/
|
|
31
|
+
export class FireworksProvider extends OpenAICompatibleProvider {
|
|
32
|
+
name = 'fireworks';
|
|
33
|
+
apiKey;
|
|
34
|
+
constructor(config = {}) {
|
|
35
|
+
const apiKey = config.apiKey ?? process.env.FIREWORKS_API_KEY;
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
throw new ProviderError('Fireworks AI API key not found. Set FIREWORKS_API_KEY environment variable or pass apiKey in config.', 'fireworks');
|
|
38
|
+
}
|
|
39
|
+
const baseConfig = {
|
|
40
|
+
baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
|
|
41
|
+
model: config.model ?? DEFAULT_MODEL,
|
|
42
|
+
maxTokens: config.maxTokens,
|
|
43
|
+
timeout: config.timeout,
|
|
44
|
+
};
|
|
45
|
+
super(baseConfig);
|
|
46
|
+
this.apiKey = apiKey;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fireworks AI authentication with Bearer token
|
|
50
|
+
*/
|
|
51
|
+
getAuthHeaders() {
|
|
52
|
+
return {
|
|
53
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Fireworks AI chat completions endpoint (OpenAI-compatible)
|
|
58
|
+
*/
|
|
59
|
+
getEndpointPath() {
|
|
60
|
+
return '/v1/chat/completions';
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Fireworks AI uses standard OpenAI body format
|
|
64
|
+
*/
|
|
65
|
+
buildProviderSpecificBody(_options) {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Map HTTP errors with Fireworks AI-specific messages
|
|
70
|
+
*/
|
|
71
|
+
mapHttpError(status, body, _model) {
|
|
72
|
+
let message = `Fireworks AI error (${String(status)})`;
|
|
73
|
+
try {
|
|
74
|
+
const parsed = JSON.parse(body);
|
|
75
|
+
if (parsed.error?.message) {
|
|
76
|
+
message = parsed.error.message;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
message = body || message;
|
|
81
|
+
}
|
|
82
|
+
switch (status) {
|
|
83
|
+
case 401:
|
|
84
|
+
return new ProviderError('Invalid Fireworks AI API key. Check your FIREWORKS_API_KEY.', 'fireworks', 401);
|
|
85
|
+
case 403:
|
|
86
|
+
return new ProviderError('Access denied. Check your Fireworks AI API key permissions.', 'fireworks', 403);
|
|
87
|
+
case 429:
|
|
88
|
+
return new ProviderError('Fireworks AI rate limit exceeded. Please wait and try again.', 'fireworks', 429);
|
|
89
|
+
case 500:
|
|
90
|
+
case 502:
|
|
91
|
+
case 503:
|
|
92
|
+
return new ProviderError('Fireworks AI service temporarily unavailable. Please try again later.', 'fireworks', status);
|
|
93
|
+
default:
|
|
94
|
+
return new ProviderError(message, 'fireworks', status);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Map connection errors with Fireworks AI-specific messages
|
|
99
|
+
*/
|
|
100
|
+
mapConnectionError(_error) {
|
|
101
|
+
return new ProviderError('Failed to connect to Fireworks AI API. Check your internet connection.', 'fireworks');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Create a Fireworks AI provider instance
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* // Using environment variable (FIREWORKS_API_KEY)
|
|
110
|
+
* const provider = createFireworksProvider();
|
|
111
|
+
*
|
|
112
|
+
* // With explicit API key
|
|
113
|
+
* const provider = createFireworksProvider({ apiKey: 'fw_...' });
|
|
114
|
+
*
|
|
115
|
+
* // With custom model
|
|
116
|
+
* const provider = createFireworksProvider({
|
|
117
|
+
* model: 'accounts/fireworks/models/llama-v3p1-70b-instruct'
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function createFireworksProvider(config = {}) {
|
|
122
|
+
return new FireworksProvider(config);
|
|
123
|
+
}
|
|
@@ -48,6 +48,12 @@ export class GeminiNativeProvider {
|
|
|
48
48
|
const { systemInstruction, contents } = this.convertMessages(messages);
|
|
49
49
|
const tools = this.convertTools(options?.tools);
|
|
50
50
|
const model = options?.model ?? this.defaultModel;
|
|
51
|
+
// Calculate payload sizes for debugging
|
|
52
|
+
const debugPayload = {
|
|
53
|
+
systemChars: systemInstruction?.length ?? 0,
|
|
54
|
+
contentsChars: JSON.stringify(contents).length,
|
|
55
|
+
toolsChars: JSON.stringify(tools).length,
|
|
56
|
+
};
|
|
51
57
|
try {
|
|
52
58
|
// Build config
|
|
53
59
|
const config = {
|
|
@@ -71,6 +77,8 @@ export class GeminiNativeProvider {
|
|
|
71
77
|
let currentToolName = '';
|
|
72
78
|
let toolInputJson = '';
|
|
73
79
|
let inThinkingBlock = false;
|
|
80
|
+
// Track the last usage metadata (Gemini sends it in EVERY chunk, not just the final one)
|
|
81
|
+
let lastUsageMetadata;
|
|
74
82
|
// Process stream chunks
|
|
75
83
|
for await (const chunk of streamResponse) {
|
|
76
84
|
const streamChunks = this.processChunk(chunk, currentToolId, currentToolName, toolInputJson, inThinkingBlock);
|
|
@@ -97,18 +105,32 @@ export class GeminiNativeProvider {
|
|
|
97
105
|
}
|
|
98
106
|
yield streamChunk;
|
|
99
107
|
}
|
|
100
|
-
//
|
|
108
|
+
// Track usage metadata (update on every chunk, yield only once at end)
|
|
101
109
|
if (chunk.usageMetadata) {
|
|
102
|
-
|
|
103
|
-
type: 'done',
|
|
104
|
-
model,
|
|
105
|
-
usage: {
|
|
106
|
-
inputTokens: chunk.usageMetadata.promptTokenCount ?? 0,
|
|
107
|
-
outputTokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
+
lastUsageMetadata = chunk.usageMetadata;
|
|
110
111
|
}
|
|
111
112
|
}
|
|
113
|
+
// Yield 'done' only ONCE after stream completes with the final usage
|
|
114
|
+
if (lastUsageMetadata) {
|
|
115
|
+
yield {
|
|
116
|
+
type: 'done',
|
|
117
|
+
model,
|
|
118
|
+
usage: {
|
|
119
|
+
inputTokens: lastUsageMetadata.promptTokenCount ?? 0,
|
|
120
|
+
outputTokens: lastUsageMetadata.candidatesTokenCount ?? 0,
|
|
121
|
+
// Gemini 2.5+ models report thinking tokens separately
|
|
122
|
+
...(lastUsageMetadata.thoughtsTokenCount
|
|
123
|
+
? { thinkingTokens: lastUsageMetadata.thoughtsTokenCount }
|
|
124
|
+
: {}),
|
|
125
|
+
// Gemini reports cached content tokens
|
|
126
|
+
...(lastUsageMetadata.cachedContentTokenCount
|
|
127
|
+
? { cacheReadTokens: lastUsageMetadata.cachedContentTokenCount }
|
|
128
|
+
: {}),
|
|
129
|
+
// Debug payload info
|
|
130
|
+
debugPayload,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
112
134
|
}
|
|
113
135
|
catch (error) {
|
|
114
136
|
throw this.mapError(error);
|
|
@@ -169,45 +191,58 @@ export class GeminiNativeProvider {
|
|
|
169
191
|
if (typeof content === 'string') {
|
|
170
192
|
return [{ text: content }];
|
|
171
193
|
}
|
|
172
|
-
|
|
194
|
+
const parts = [];
|
|
195
|
+
for (const block of content) {
|
|
173
196
|
switch (block.type) {
|
|
174
197
|
case 'text':
|
|
175
|
-
|
|
198
|
+
parts.push({ text: block.text });
|
|
199
|
+
break;
|
|
176
200
|
case 'tool_use':
|
|
177
201
|
// Convert to functionCall with thought signature if present
|
|
178
202
|
// Gemini 3 requires thought signatures on function calls
|
|
179
|
-
|
|
203
|
+
// Note: No 'id' field - Gemini uses 'name' for matching
|
|
204
|
+
parts.push({
|
|
180
205
|
functionCall: {
|
|
181
|
-
id: block.id,
|
|
182
206
|
name: block.name,
|
|
183
207
|
args: block.input,
|
|
184
208
|
},
|
|
185
209
|
// Include thought signature if present (required for Gemini 3)
|
|
186
210
|
...(block.signature ? { thoughtSignature: block.signature } : {}),
|
|
187
|
-
};
|
|
188
|
-
|
|
211
|
+
});
|
|
212
|
+
break;
|
|
213
|
+
case 'tool_result': {
|
|
189
214
|
// Convert to functionResponse
|
|
190
|
-
|
|
215
|
+
// block.content is a JSON string from the agent - parse it for the API
|
|
216
|
+
let responseData;
|
|
217
|
+
try {
|
|
218
|
+
responseData = JSON.parse(block.content);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// If not valid JSON, wrap the string in a result object
|
|
222
|
+
responseData = { result: block.content };
|
|
223
|
+
}
|
|
224
|
+
parts.push({
|
|
191
225
|
functionResponse: {
|
|
192
|
-
|
|
226
|
+
// Note: No 'id' field - Gemini matches by 'name' only
|
|
193
227
|
name: this.extractToolName(content, block.toolUseId),
|
|
194
|
-
response:
|
|
228
|
+
response: responseData,
|
|
195
229
|
},
|
|
196
|
-
};
|
|
230
|
+
});
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
197
233
|
case 'thinking':
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
thoughtSignature: block.signature,
|
|
203
|
-
};
|
|
234
|
+
// Thinking blocks should NOT be sent back to Gemini.
|
|
235
|
+
// They are internal model reasoning. Only the signature on function calls matters.
|
|
236
|
+
// Skip - do not add to parts.
|
|
237
|
+
break;
|
|
204
238
|
default: {
|
|
205
239
|
// Exhaustive check
|
|
206
240
|
const _exhaustive = block;
|
|
207
241
|
throw new Error(`Unknown content block type: ${_exhaustive.type}`);
|
|
208
242
|
}
|
|
209
243
|
}
|
|
210
|
-
}
|
|
244
|
+
}
|
|
245
|
+
return parts;
|
|
211
246
|
}
|
|
212
247
|
/**
|
|
213
248
|
* Extract tool name from tool_use block that matches the given toolUseId
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Groq LLM Provider
|
|
3
|
+
*
|
|
4
|
+
* Implements LLMProvider interface for Groq models.
|
|
5
|
+
* Extends OpenAICompatibleProvider for shared functionality.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const provider = createGroqProvider({
|
|
10
|
+
* model: 'llama-3.2-90b-vision-preview',
|
|
11
|
+
* apiKey: process.env.GROQ_API_KEY
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* - Requires valid Groq API key
|
|
17
|
+
* - Default model is llama-3.2-8b-preview
|
|
18
|
+
* - Known for extremely fast inference speeds
|
|
19
|
+
*/
|
|
20
|
+
import type { ChatOptions } from './types.js';
|
|
21
|
+
import { ProviderError } from '../errors.js';
|
|
22
|
+
import { OpenAICompatibleProvider } from './openai-compatible.js';
|
|
23
|
+
/**
|
|
24
|
+
* Configuration for GroqProvider
|
|
25
|
+
*/
|
|
26
|
+
export interface GroqProviderConfig {
|
|
27
|
+
/** Groq API key (falls back to GROQ_API_KEY env var) */
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
/** Base URL for Groq API (default: https://api.groq.com/openai) */
|
|
30
|
+
baseUrl?: string;
|
|
31
|
+
/** Default model to use (default: llama-3.2-8b-preview) */
|
|
32
|
+
model?: string;
|
|
33
|
+
/** Default max tokens (default: 4096) */
|
|
34
|
+
maxTokens?: number;
|
|
35
|
+
/** Request timeout in milliseconds (default: 120000) */
|
|
36
|
+
timeout?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Groq LLM Provider
|
|
40
|
+
*
|
|
41
|
+
* Provides streaming chat completion using Groq's ultra-fast inference.
|
|
42
|
+
* Supports Llama, Mixtral, and other models optimized for speed.
|
|
43
|
+
*/
|
|
44
|
+
export declare class GroqProvider extends OpenAICompatibleProvider {
|
|
45
|
+
readonly name = "groq";
|
|
46
|
+
private readonly apiKey;
|
|
47
|
+
constructor(config?: GroqProviderConfig);
|
|
48
|
+
/**
|
|
49
|
+
* Groq authentication with Bearer token
|
|
50
|
+
*/
|
|
51
|
+
protected getAuthHeaders(): Record<string, string>;
|
|
52
|
+
/**
|
|
53
|
+
* Groq chat completions endpoint (OpenAI-compatible)
|
|
54
|
+
*/
|
|
55
|
+
protected getEndpointPath(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Groq uses standard OpenAI body format
|
|
58
|
+
*/
|
|
59
|
+
protected buildProviderSpecificBody(_options?: ChatOptions): Record<string, unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Map HTTP errors with Groq-specific messages
|
|
62
|
+
*/
|
|
63
|
+
protected mapHttpError(status: number, body: string, _model: string): ProviderError;
|
|
64
|
+
/**
|
|
65
|
+
* Map connection errors with Groq-specific messages
|
|
66
|
+
*/
|
|
67
|
+
protected mapConnectionError(_error: Error): ProviderError;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a Groq provider instance
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Using environment variable (GROQ_API_KEY)
|
|
75
|
+
* const provider = createGroqProvider();
|
|
76
|
+
*
|
|
77
|
+
* // With explicit API key
|
|
78
|
+
* const provider = createGroqProvider({ apiKey: 'gsk_...' });
|
|
79
|
+
*
|
|
80
|
+
* // With custom model
|
|
81
|
+
* const provider = createGroqProvider({
|
|
82
|
+
* model: 'llama-3.2-90b-vision-preview'
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export declare function createGroqProvider(config?: GroqProviderConfig): GroqProvider;
|