@ank1015/providers 0.0.1 → 0.0.3
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/LICENSE +21 -0
- package/README.md +93 -383
- package/dist/agent/conversation.d.ts +97 -0
- package/dist/agent/conversation.d.ts.map +1 -0
- package/dist/agent/conversation.js +328 -0
- package/dist/agent/conversation.js.map +1 -0
- package/dist/agent/runner.d.ts +37 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +169 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/tools/calculate.d.ts +15 -0
- package/dist/agent/tools/calculate.d.ts.map +1 -0
- package/dist/agent/tools/calculate.js +23 -0
- package/dist/agent/tools/calculate.js.map +1 -0
- package/dist/agent/tools/get-current-time.d.ts +15 -0
- package/dist/agent/tools/get-current-time.d.ts.map +1 -0
- package/dist/agent/tools/get-current-time.js +38 -0
- package/dist/agent/tools/get-current-time.js.map +1 -0
- package/dist/agent/tools/index.d.ts +3 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +3 -0
- package/dist/agent/tools/index.js.map +1 -0
- package/dist/agent/types.d.ts +53 -31
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +1 -2
- package/dist/agent/utils.d.ts +14 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +59 -0
- package/dist/agent/utils.js.map +1 -0
- package/dist/index.d.ts +16 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/llm.d.ts +15 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +92 -0
- package/dist/llm.js.map +1 -0
- package/dist/models.d.ts +8 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +25 -112
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +72 -227
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +30 -32
- package/dist/models.js.map +1 -1
- package/dist/providers/google/complete.d.ts +3 -0
- package/dist/providers/google/complete.d.ts.map +1 -0
- package/dist/providers/google/complete.js +53 -0
- package/dist/providers/google/complete.js.map +1 -0
- package/dist/providers/google/index.d.ts +6 -0
- package/dist/providers/google/index.d.ts.map +1 -0
- package/dist/providers/google/index.js +6 -0
- package/dist/providers/google/index.js.map +1 -0
- package/dist/providers/google/stream.d.ts +3 -0
- package/dist/providers/google/stream.d.ts.map +1 -0
- package/dist/providers/{google.js → google/stream.js} +67 -231
- package/dist/providers/google/stream.js.map +1 -0
- package/dist/providers/google/types.d.ts +8 -0
- package/dist/providers/google/types.d.ts.map +1 -0
- package/dist/providers/google/types.js +2 -0
- package/dist/providers/google/types.js.map +1 -0
- package/dist/providers/google/utils.d.ts +30 -0
- package/dist/providers/google/utils.d.ts.map +1 -0
- package/dist/providers/google/utils.js +354 -0
- package/dist/providers/google/utils.js.map +1 -0
- package/dist/providers/openai/complete.d.ts +3 -0
- package/dist/providers/openai/complete.d.ts.map +1 -0
- package/dist/providers/openai/complete.js +57 -0
- package/dist/providers/openai/complete.js.map +1 -0
- package/dist/providers/openai/index.d.ts +4 -0
- package/dist/providers/openai/index.d.ts.map +1 -0
- package/dist/providers/openai/index.js +4 -0
- package/dist/providers/openai/index.js.map +1 -0
- package/dist/providers/openai/stream.d.ts +3 -0
- package/dist/providers/openai/stream.d.ts.map +1 -0
- package/dist/providers/{openai.js → openai/stream.js} +74 -152
- package/dist/providers/openai/stream.js.map +1 -0
- package/dist/providers/openai/types.d.ts +8 -0
- package/dist/providers/openai/types.d.ts.map +1 -0
- package/dist/providers/openai/types.js +2 -0
- package/dist/providers/openai/types.js.map +1 -0
- package/dist/providers/openai/utils.d.ts +13 -0
- package/dist/providers/openai/utils.d.ts.map +1 -0
- package/dist/providers/openai/utils.js +285 -0
- package/dist/providers/openai/utils.js.map +1 -0
- package/dist/types.d.ts +95 -87
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -9
- package/dist/types.js.map +1 -1
- package/dist/utils/event-stream.d.ts +2 -2
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +2 -7
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/json-parse.js +3 -6
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/overflow.d.ts +51 -0
- package/dist/utils/overflow.d.ts.map +1 -0
- package/dist/utils/overflow.js +106 -0
- package/dist/utils/overflow.js.map +1 -0
- package/dist/utils/sanitize-unicode.js +1 -4
- package/dist/utils/sanitize-unicode.js.map +1 -1
- package/dist/utils/uuid.d.ts +6 -0
- package/dist/utils/uuid.d.ts.map +1 -0
- package/dist/utils/uuid.js +9 -0
- package/dist/utils/uuid.js.map +1 -0
- package/dist/utils/validation.d.ts +10 -3
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +20 -12
- package/dist/utils/validation.js.map +1 -1
- package/package.json +47 -8
- package/biome.json +0 -43
- package/dist/agent/agent-loop.d.ts +0 -5
- package/dist/agent/agent-loop.d.ts.map +0 -1
- package/dist/agent/agent-loop.js +0 -219
- package/dist/agent/agent-loop.js.map +0 -1
- package/dist/providers/convert.d.ts +0 -6
- package/dist/providers/convert.d.ts.map +0 -1
- package/dist/providers/convert.js +0 -207
- package/dist/providers/convert.js.map +0 -1
- package/dist/providers/google.d.ts +0 -26
- package/dist/providers/google.d.ts.map +0 -1
- package/dist/providers/google.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -17
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/providers/openai.js.map +0 -1
- package/dist/stream.d.ts +0 -4
- package/dist/stream.d.ts.map +0 -1
- package/dist/stream.js +0 -40
- package/dist/stream.js.map +0 -1
- package/dist/test-google-agent-loop.d.ts +0 -2
- package/dist/test-google-agent-loop.d.ts.map +0 -1
- package/dist/test-google-agent-loop.js +0 -186
- package/dist/test-google-agent-loop.js.map +0 -1
- package/dist/test-google.d.ts +0 -2
- package/dist/test-google.d.ts.map +0 -1
- package/dist/test-google.js +0 -41
- package/dist/test-google.js.map +0 -1
- package/src/agent/agent-loop.ts +0 -275
- package/src/agent/types.ts +0 -80
- package/src/index.ts +0 -72
- package/src/models.generated.ts +0 -314
- package/src/models.ts +0 -45
- package/src/providers/convert.ts +0 -222
- package/src/providers/google.ts +0 -496
- package/src/providers/openai.ts +0 -437
- package/src/stream.ts +0 -60
- package/src/types.ts +0 -198
- package/src/utils/event-stream.ts +0 -60
- package/src/utils/json-parse.ts +0 -28
- package/src/utils/sanitize-unicode.ts +0 -25
- package/src/utils/validation.ts +0 -69
- package/test/core/agent-loop.test.ts +0 -958
- package/test/core/stream.test.ts +0 -409
- package/test/data/red-circle.png +0 -0
- package/test/data/superintelligentwill.pdf +0 -0
- package/test/edge-cases/general.test.ts +0 -565
- package/test/integration/e2e.test.ts +0 -530
- package/test/models/cost.test.ts +0 -499
- package/test/models/registry.test.ts +0 -298
- package/test/providers/convert.test.ts +0 -846
- package/test/providers/google-schema.test.ts +0 -666
- package/test/providers/google-stream.test.ts +0 -369
- package/test/providers/openai-stream.test.ts +0 -251
- package/test/utils/event-stream.test.ts +0 -289
- package/test/utils/json-parse.test.ts +0 -344
- package/test/utils/sanitize-unicode.test.ts +0 -329
- package/test/utils/validation.test.ts +0 -614
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mario Zechner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A provider abstraction layer for building agentic systems with multiple LLM providers. Built with a philosophy that **harnesses should be model-specific** while maintaining the ability to test and compose different models together.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Most of the coding patterns are taken and inspired from [PI-mono](https://github.com/badlogic/pi-mono/tree/main)
|
|
6
6
|
|
|
7
7
|
## Philosophy
|
|
8
8
|
|
|
@@ -23,16 +23,7 @@ We achieve this by:
|
|
|
23
23
|
|
|
24
24
|
- **🎯 Provider-Specific Implementations**: Each provider follows its own best practices
|
|
25
25
|
- **🔄 Unified Streaming API**: Standardized event stream across all providers
|
|
26
|
-
-
|
|
27
|
-
- **🛠️ Type-Safe Tools**: TypeBox-powered JSON Schema validation
|
|
28
|
-
- **💰 Cost Tracking**: Automatic token usage and cost calculation
|
|
29
|
-
- **⚡ Real-Time Streaming**: 21 granular event types for UI updates
|
|
30
|
-
- **🎨 Extended Thinking**: Support for reasoning/thinking modes
|
|
31
|
-
|
|
32
|
-
## Supported Providers
|
|
33
|
-
|
|
34
|
-
- **OpenAI**: GPT-5 series (Codex, Mini, Pro) with prompt caching and reasoning
|
|
35
|
-
- **Google**: Gemini 2.5 Flash, Gemini 3 Pro with extended thinking
|
|
26
|
+
- **💾 State Management**: Robust `Conversation` class to manage chat history, message queuing, and state persistence.
|
|
36
27
|
|
|
37
28
|
## Installation
|
|
38
29
|
|
|
@@ -40,414 +31,133 @@ We achieve this by:
|
|
|
40
31
|
npm install @ank1015/providers
|
|
41
32
|
```
|
|
42
33
|
|
|
43
|
-
Set up your API keys:
|
|
44
|
-
```bash
|
|
45
|
-
export OPENAI_API_KEY="your-key"
|
|
46
|
-
export GEMINI_API_KEY="your-key"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
34
|
## Quick Start
|
|
50
35
|
|
|
51
|
-
|
|
36
|
+
Here's how to create a simple agent that can perform calculations.
|
|
52
37
|
|
|
53
38
|
```typescript
|
|
54
|
-
import {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
39
|
+
import { Conversation, calculateTool } from "@ank1015/providers";
|
|
40
|
+
|
|
41
|
+
async function main() {
|
|
42
|
+
// 1. Initialize Conversation
|
|
43
|
+
// By default uses Gemini Flash, but you can configure any model
|
|
44
|
+
const conversation = new Conversation();
|
|
45
|
+
|
|
46
|
+
// 2. Add Tools
|
|
47
|
+
// The SDK includes sample tools like 'calculate' for testing
|
|
48
|
+
conversation.setTools([calculateTool]);
|
|
49
|
+
|
|
50
|
+
// 3. Prompt the Agent
|
|
51
|
+
console.log("User: What is (123 * 45) + 9?");
|
|
52
|
+
const messages = await conversation.prompt("What is (123 * 45) + 9?");
|
|
53
|
+
|
|
54
|
+
// 4. Get the result
|
|
55
|
+
const lastMessage = messages[messages.length - 1];
|
|
56
|
+
|
|
57
|
+
// content is an array of typed blocks (text, image, toolUse, etc.)
|
|
58
|
+
const responseContent = lastMessage.content.find(c => c.type === 'response');
|
|
59
|
+
|
|
60
|
+
if (responseContent?.content[0].type === 'text') {
|
|
61
|
+
console.log("Agent:", responseContent.content[0].content);
|
|
72
62
|
}
|
|
73
63
|
}
|
|
74
|
-
|
|
75
|
-
// Get the final native message (preserves provider-specific state)
|
|
76
|
-
const nativeMessage = await response.result();
|
|
77
64
|
```
|
|
78
65
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { agentLoop, defineTool, MODELS } from "@ank1015/providers";
|
|
83
|
-
import { Type } from "@sinclair/typebox";
|
|
84
|
-
|
|
85
|
-
// Define tools with type-safe schemas
|
|
86
|
-
const tools = [
|
|
87
|
-
defineTool({
|
|
88
|
-
name: "calculator",
|
|
89
|
-
description: "Perform mathematical calculations",
|
|
90
|
-
parameters: Type.Object({
|
|
91
|
-
expression: Type.String({ description: "Math expression to evaluate" }),
|
|
92
|
-
}),
|
|
93
|
-
}),
|
|
94
|
-
] as const;
|
|
95
|
-
|
|
96
|
-
// Create agent tools with execution logic
|
|
97
|
-
const agentTools = tools.map((tool) => ({
|
|
98
|
-
...tool,
|
|
99
|
-
label: "Calculator",
|
|
100
|
-
async execute(toolCallId, params, signal) {
|
|
101
|
-
const result = eval(params.expression); // Use a safe eval in production!
|
|
102
|
-
return {
|
|
103
|
-
content: [{ type: "text", content: `Result: ${result}` }],
|
|
104
|
-
details: { result },
|
|
105
|
-
};
|
|
106
|
-
},
|
|
107
|
-
}));
|
|
108
|
-
|
|
109
|
-
const context = {
|
|
110
|
-
systemPrompt: "You are a helpful assistant with access to a calculator.",
|
|
111
|
-
messages: [],
|
|
112
|
-
tools: agentTools,
|
|
113
|
-
};
|
|
66
|
+
## Usage
|
|
114
67
|
|
|
115
|
-
|
|
116
|
-
role: "user" as const,
|
|
117
|
-
content: [{ type: "text" as const, content: "What is 156 * 234?" }],
|
|
118
|
-
timestamp: Date.now(),
|
|
119
|
-
};
|
|
68
|
+
### 1. Configuration & Providers
|
|
120
69
|
|
|
121
|
-
|
|
122
|
-
model: MODELS.OPENAI_GPT5_MINI,
|
|
123
|
-
providerOptions: {},
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const eventStream = agentLoop(prompt, context, config);
|
|
127
|
-
|
|
128
|
-
for await (const event of eventStream) {
|
|
129
|
-
switch (event.type) {
|
|
130
|
-
case "message_update":
|
|
131
|
-
// Handle streaming assistant message
|
|
132
|
-
console.log("Assistant:", event.message);
|
|
133
|
-
break;
|
|
134
|
-
case "tool_execution_start":
|
|
135
|
-
console.log(`Executing tool: ${event.toolName}`);
|
|
136
|
-
break;
|
|
137
|
-
case "tool_execution_end":
|
|
138
|
-
console.log(`Result:`, event.result);
|
|
139
|
-
break;
|
|
140
|
-
case "agent_end":
|
|
141
|
-
console.log("Agent completed with status:", event.status);
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Get all messages for conversation history
|
|
147
|
-
const allMessages = await eventStream.result();
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### Working with Different Providers
|
|
70
|
+
You can switch providers easily by setting the provider configuration.
|
|
151
71
|
|
|
152
72
|
```typescript
|
|
153
|
-
import {
|
|
154
|
-
|
|
155
|
-
// OpenAI with reasoning
|
|
156
|
-
const openaiResponse = stream(MODELS.OPENAI_GPT5_MINI, context, {
|
|
157
|
-
reasoning: {
|
|
158
|
-
effort: "medium",
|
|
159
|
-
summaryStyle: "concise",
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// Google with thinking
|
|
164
|
-
const googleResponse = stream(MODELS.GOOGLE_GEMINI_2_5_FLASH, context, {
|
|
165
|
-
thinkingConfig: { extendedThinking: { level: "EXTENDED_THINKING_THINK_MODE" } },
|
|
166
|
-
temperature: 0.7,
|
|
167
|
-
});
|
|
73
|
+
import { Conversation } from "@ank1015/providers";
|
|
74
|
+
import { getModel } from "@ank1015/providers/models";
|
|
168
75
|
|
|
169
|
-
|
|
170
|
-
for await (const event of openaiResponse) {
|
|
171
|
-
// Handle events the same way regardless of provider
|
|
172
|
-
}
|
|
173
|
-
```
|
|
76
|
+
const conversation = new Conversation();
|
|
174
77
|
|
|
175
|
-
|
|
78
|
+
// Switch to OpenAI GPT-5.2 (Example Model ID from registry)
|
|
79
|
+
const openAIModel = getModel('openai', 'gpt-5.2');
|
|
176
80
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
console.log(`Output: ${usage.output} ($${usage.cost.output.toFixed(4)})`);
|
|
185
|
-
if (usage.cacheRead > 0) {
|
|
186
|
-
console.log(`Cache Read: ${usage.cacheRead} ($${usage.cost.cacheRead.toFixed(4)})`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
81
|
+
if (openAIModel) {
|
|
82
|
+
conversation.setProvider({
|
|
83
|
+
model: openAIModel,
|
|
84
|
+
providerOptions: {
|
|
85
|
+
apiKey: process.env.OPENAI_API_KEY
|
|
86
|
+
}
|
|
87
|
+
});
|
|
189
88
|
}
|
|
190
89
|
```
|
|
191
90
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
### Message Storage Strategy
|
|
195
|
-
|
|
196
|
-
```
|
|
197
|
-
User Message ────────> Assistant Message ────────> Tool Result
|
|
198
|
-
Standardized Native Provider Standardized
|
|
199
|
-
✓ Can rebuild ✗ Store as-is ✓ Can rebuild
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
- **User Messages** & **Tool Results**: Stored in standardized format, can be converted to any provider
|
|
203
|
-
- **Assistant Messages**: Stored in native provider format to preserve:
|
|
204
|
-
- Prompt caching state
|
|
205
|
-
- Thinking traces
|
|
206
|
-
- Internal provider state
|
|
207
|
-
- Response metadata
|
|
208
|
-
|
|
209
|
-
### Streaming Events
|
|
91
|
+
### 2. Defining Custom Tools
|
|
210
92
|
|
|
211
|
-
|
|
93
|
+
Tools are defined using `TypeBox` for schema validation. This ensures the LLM generates arguments that match your code's expectations.
|
|
212
94
|
|
|
213
95
|
```typescript
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
| { type: "text_start"; contentIndex: number; partial: AssistantMessage }
|
|
217
|
-
| { type: "text_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
218
|
-
| { type: "text_end"; contentIndex: number; content: string; partial: AssistantMessage }
|
|
219
|
-
| { type: "thinking_start"; contentIndex: number; partial: AssistantMessage }
|
|
220
|
-
| { type: "thinking_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
221
|
-
| { type: "thinking_end"; contentIndex: number; content: string; partial: AssistantMessage }
|
|
222
|
-
| { type: "toolcall_start"; contentIndex: number; partial: AssistantMessage }
|
|
223
|
-
| { type: "toolcall_delta"; contentIndex: number; delta: string; partial: AssistantMessage }
|
|
224
|
-
| { type: "toolcall_end"; contentIndex: number; toolCall: AssistantToolCall; partial: AssistantMessage }
|
|
225
|
-
| { type: "done"; reason: StopReason; message: AssistantMessage }
|
|
226
|
-
| { type: "error"; reason: StopReason; error: AssistantMessage };
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
### Agent Loop Flow
|
|
230
|
-
|
|
231
|
-
```
|
|
232
|
-
Initial Prompt
|
|
233
|
-
↓
|
|
234
|
-
┌─────────────────────────┐
|
|
235
|
-
│ While tool calls exist:│
|
|
236
|
-
│ 1. Stream response │
|
|
237
|
-
│ 2. Execute tools │
|
|
238
|
-
│ 3. Inject results │
|
|
239
|
-
│ 4. Repeat │
|
|
240
|
-
└─────────────────────────┘
|
|
241
|
-
↓
|
|
242
|
-
Return all messages
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
The agent loop automatically handles multi-turn conversations with tool execution, preserving full conversation state.
|
|
246
|
-
|
|
247
|
-
## API Reference
|
|
248
|
-
|
|
249
|
-
### Core Functions
|
|
250
|
-
|
|
251
|
-
#### `stream(model, context, options)`
|
|
252
|
-
|
|
253
|
-
Stream an LLM response with standardized events.
|
|
254
|
-
|
|
255
|
-
- **model**: Model object from `MODELS` registry
|
|
256
|
-
- **context**: Conversation context with messages, system prompt, and tools
|
|
257
|
-
- **options**: Provider-specific options (apiKey, temperature, reasoning, etc.)
|
|
258
|
-
- **returns**: `AssistantMessageEventStream` (async iterable)
|
|
259
|
-
|
|
260
|
-
#### `agentLoop(prompt, context, config, signal)`
|
|
261
|
-
|
|
262
|
-
Run a multi-turn agent loop with automatic tool execution.
|
|
263
|
-
|
|
264
|
-
- **prompt**: Initial user message
|
|
265
|
-
- **context**: Agent context with messages, system prompt, and agent tools
|
|
266
|
-
- **config**: Configuration with model, provider options, and optional preprocessor
|
|
267
|
-
- **signal**: Optional AbortSignal for cancellation
|
|
268
|
-
- **returns**: `EventStream<AgentEvent>` (async iterable)
|
|
269
|
-
|
|
270
|
-
### Types
|
|
271
|
-
|
|
272
|
-
#### `Message`
|
|
273
|
-
|
|
274
|
-
Union type for all message types:
|
|
275
|
-
```typescript
|
|
276
|
-
type Message = UserMessage | NativeAssistantMessage | ToolResultMessage
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
#### `Tool`
|
|
280
|
-
|
|
281
|
-
Type-safe tool definition:
|
|
282
|
-
```typescript
|
|
283
|
-
interface Tool<TParameters extends TSchema = TSchema> {
|
|
284
|
-
name: string;
|
|
285
|
-
description: string;
|
|
286
|
-
parameters: TParameters; // TypeBox JSON Schema
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
#### `AgentTool`
|
|
291
|
-
|
|
292
|
-
Extended tool with execution logic:
|
|
293
|
-
```typescript
|
|
294
|
-
interface AgentTool extends Tool {
|
|
295
|
-
label: string;
|
|
296
|
-
execute(
|
|
297
|
-
toolCallId: string,
|
|
298
|
-
params: Static<TParameters>,
|
|
299
|
-
signal?: AbortSignal
|
|
300
|
-
): Promise<AgentToolResult>;
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Utilities
|
|
305
|
-
|
|
306
|
-
#### `defineTool(tool)`
|
|
96
|
+
import { Type } from "@sinclair/typebox";
|
|
97
|
+
import type { AgentTool } from "@ank1015/providers/agent/types";
|
|
307
98
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
description: "Search the web",
|
|
313
|
-
parameters: Type.Object({
|
|
314
|
-
query: Type.String(),
|
|
315
|
-
}),
|
|
99
|
+
// 1. Define the Schema
|
|
100
|
+
const getWeatherSchema = Type.Object({
|
|
101
|
+
location: Type.String({ description: "The city and state, e.g. San Francisco, CA" }),
|
|
102
|
+
unit: Type.Optional(Type.Union([Type.Literal("celsius"), Type.Literal("fahrenheit")]))
|
|
316
103
|
});
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
#### `calculateCost(model, usage)`
|
|
320
104
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
105
|
+
// 2. Create the Tool Definition
|
|
106
|
+
export const getWeatherTool: AgentTool<typeof getWeatherSchema> = {
|
|
107
|
+
name: "get_weather",
|
|
108
|
+
label: "Get Weather",
|
|
109
|
+
description: "Get the current weather for a location",
|
|
110
|
+
parameters: getWeatherSchema,
|
|
111
|
+
// 3. Implement Execution Logic
|
|
112
|
+
execute: async (toolCallId, args) => {
|
|
113
|
+
// args is fully typed here!
|
|
114
|
+
const { location, unit } = args;
|
|
115
|
+
|
|
116
|
+
// Mock API call
|
|
117
|
+
return {
|
|
118
|
+
content: [{ type: "text", content: `Sunny, 25°C in ${location}` }],
|
|
119
|
+
details: { temp: 25, condition: "Sunny" }
|
|
120
|
+
};
|
|
121
|
+
}
|
|
332
122
|
};
|
|
333
123
|
|
|
334
|
-
|
|
335
|
-
|
|
124
|
+
// 4. Register with Conversation
|
|
125
|
+
conversation.setTools([getWeatherTool]);
|
|
336
126
|
```
|
|
337
127
|
|
|
338
|
-
|
|
128
|
+
### 3. Streaming Events
|
|
339
129
|
|
|
340
|
-
|
|
130
|
+
Subscribe to the conversation to receive real-time updates. This is crucial for building responsive UIs that show "thinking" states or streaming text.
|
|
341
131
|
|
|
342
132
|
```typescript
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
extendedThinking: {
|
|
362
|
-
level: "EXTENDED_THINKING_THINK_MODE",
|
|
363
|
-
},
|
|
364
|
-
},
|
|
365
|
-
responseMimeType: "application/json",
|
|
366
|
-
imageConfig: {
|
|
367
|
-
aspectRatio: "ASPECT_RATIO_16_9",
|
|
368
|
-
size: "LARGE",
|
|
369
|
-
},
|
|
370
|
-
maxOutputTokens: 8192,
|
|
371
|
-
temperature: 0.7,
|
|
133
|
+
conversation.subscribe((event) => {
|
|
134
|
+
switch (event.type) {
|
|
135
|
+
case "message_update":
|
|
136
|
+
// Can handle 'thinking_delta', 'text_delta', etc. inside the event.message
|
|
137
|
+
const msg = event.message;
|
|
138
|
+
if (msg.type === 'text_delta') {
|
|
139
|
+
process.stdout.write(msg.delta);
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
|
|
143
|
+
case "tool_execution_start":
|
|
144
|
+
console.log(`\nTool ${event.toolName} started...`);
|
|
145
|
+
break;
|
|
146
|
+
|
|
147
|
+
case "tool_execution_end":
|
|
148
|
+
console.log(`Tool ${event.toolName} finished.`);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
372
151
|
});
|
|
373
152
|
```
|
|
374
153
|
|
|
375
|
-
##
|
|
376
|
-
|
|
377
|
-
1. **Model-Specific Best Practices**: Each provider implementation follows the provider's recommended patterns
|
|
378
|
-
2. **State Preservation**: Native assistant messages preserve all provider-specific state
|
|
379
|
-
3. **Type Safety**: TypeBox schemas provide compile-time and runtime validation
|
|
380
|
-
4. **Stream-First**: All operations are async and support real-time updates
|
|
381
|
-
5. **Cost Transparency**: Every response includes detailed token usage and costs
|
|
382
|
-
6. **Graceful Degradation**: Validation falls back gracefully in restricted environments
|
|
383
|
-
7. **Developer Experience**: Rich type inference and autocomplete support
|
|
384
|
-
|
|
385
|
-
## Development
|
|
386
|
-
|
|
387
|
-
```bash
|
|
388
|
-
# Install dependencies
|
|
389
|
-
npm install
|
|
390
|
-
|
|
391
|
-
# Build
|
|
392
|
-
npm run build
|
|
393
|
-
|
|
394
|
-
# Watch mode
|
|
395
|
-
npm run dev
|
|
396
|
-
|
|
397
|
-
# Run tests
|
|
398
|
-
npm run test
|
|
399
|
-
|
|
400
|
-
# Lint and type check
|
|
401
|
-
npm run check
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
## Project Structure
|
|
405
|
-
|
|
406
|
-
```
|
|
407
|
-
src/
|
|
408
|
-
├── index.ts # Main exports
|
|
409
|
-
├── types.ts # Core type definitions
|
|
410
|
-
├── stream.ts # Provider routing
|
|
411
|
-
├── models.ts # Cost calculation
|
|
412
|
-
├── models.generated.ts # Model registry
|
|
413
|
-
├── agent/
|
|
414
|
-
│ ├── agent-loop.ts # Multi-turn agent orchestration
|
|
415
|
-
│ └── types.ts # Agent-specific types
|
|
416
|
-
├── providers/
|
|
417
|
-
│ ├── openai.ts # OpenAI implementation
|
|
418
|
-
│ ├── google.ts # Google implementation
|
|
419
|
-
│ └── convert.ts # Message format conversion
|
|
420
|
-
└── utils/
|
|
421
|
-
├── event-stream.ts # Async event streaming
|
|
422
|
-
├── validation.ts # Tool argument validation
|
|
423
|
-
├── json-parse.ts # Streaming JSON parser
|
|
424
|
-
└── sanitize-unicode.ts # Unicode sanitization
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
## Why This Architecture?
|
|
428
|
-
|
|
429
|
-
Traditional LLM abstraction layers try to make all models interchangeable, leading to:
|
|
430
|
-
- Lost provider-specific features (caching, thinking traces)
|
|
431
|
-
- Lowest-common-denominator APIs
|
|
432
|
-
- Poor utilization of each model's strengths
|
|
433
|
-
|
|
434
|
-
Our approach:
|
|
435
|
-
- ✅ Build provider-specific implementations following best practices
|
|
436
|
-
- ✅ Preserve native state for optimal performance
|
|
437
|
-
- ✅ Provide standardized interfaces for development flexibility
|
|
438
|
-
- ✅ Enable model composition without forcing model switching
|
|
439
|
-
- ✅ Support forking/conversion when truly needed
|
|
440
|
-
|
|
441
|
-
The result: **You get the best of both worlds** - full provider capabilities without vendor lock-in.
|
|
442
|
-
|
|
443
|
-
## Contributing
|
|
154
|
+
## Architecture
|
|
444
155
|
|
|
445
|
-
|
|
156
|
+
- **`Conversation`**: The high-level state manager. It tracks the message history (`Memory`), handles message queuing (for handling rapid user inputs), and manages the `AgentRunner`.
|
|
157
|
+
- **`AgentRunner`**: A stateless engine that executes the "Agent Protocol". It sends messages to the LLM, parses tool calls from the response, executes the tools, and feeds the results back to the LLM until a final response is reached or the loop terminates.
|
|
158
|
+
- **`LLMClient`**: The low-level abstraction that standardizes API calls to OpenAI, Google, etc.
|
|
159
|
+
- **`Utils`**: Includes powerful helpers like `parseStreamingJson` (for real-time tool visualization) and `isContextOverflow` (for handling token limits).
|
|
446
160
|
|
|
447
161
|
## License
|
|
448
162
|
|
|
449
163
|
MIT
|
|
450
|
-
|
|
451
|
-
---
|
|
452
|
-
|
|
453
|
-
**Built for developers who want to harness the full power of frontier models without sacrificing flexibility.**
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Api, Message } from "../types.js";
|
|
2
|
+
import { AgentEvent, AgentState, Attachment, Provider } from "./types.js";
|
|
3
|
+
import { LLMClient } from "../llm.js";
|
|
4
|
+
import { AgentRunner } from "./runner.js";
|
|
5
|
+
export interface AgentOptions {
|
|
6
|
+
initialState?: Partial<AgentState>;
|
|
7
|
+
messageTransformer?: (messages: Message[]) => Message[] | Promise<Message[]>;
|
|
8
|
+
queueMode?: "all" | "one-at-a-time";
|
|
9
|
+
client?: LLMClient;
|
|
10
|
+
runner?: AgentRunner;
|
|
11
|
+
}
|
|
12
|
+
export declare class Conversation {
|
|
13
|
+
private client;
|
|
14
|
+
private runner;
|
|
15
|
+
private _state;
|
|
16
|
+
private listeners;
|
|
17
|
+
private abortController?;
|
|
18
|
+
private messageTransformer;
|
|
19
|
+
private messageQueue;
|
|
20
|
+
private queueMode;
|
|
21
|
+
private runningPrompt?;
|
|
22
|
+
private resolveRunningPrompt?;
|
|
23
|
+
private streamAssistantMessage;
|
|
24
|
+
constructor(opts?: AgentOptions);
|
|
25
|
+
get state(): AgentState;
|
|
26
|
+
subscribe(fn: (e: AgentEvent) => void): () => void;
|
|
27
|
+
private emit;
|
|
28
|
+
setStreamAssistantMessage(stream: boolean): void;
|
|
29
|
+
setSystemPrompt(v: string): void;
|
|
30
|
+
setProvider<TApi extends Api>(provider: Provider<TApi>): void;
|
|
31
|
+
setQueueMode(mode: "all" | "one-at-a-time"): void;
|
|
32
|
+
getQueueMode(): "all" | "one-at-a-time";
|
|
33
|
+
setTools(t: typeof this._state.tools): void;
|
|
34
|
+
replaceMessages(ms: Message[]): void;
|
|
35
|
+
appendMessage(m: Message): void;
|
|
36
|
+
appendMessages(ms: Message[]): void;
|
|
37
|
+
queueMessage(m: Message): Promise<void>;
|
|
38
|
+
clearMessageQueue(): void;
|
|
39
|
+
clearMessages(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Remove all event listeners.
|
|
42
|
+
*/
|
|
43
|
+
clearListeners(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Remove a message by its ID.
|
|
46
|
+
* @returns true if the message was found and removed, false otherwise.
|
|
47
|
+
*/
|
|
48
|
+
removeMessage(messageId: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Update a message by its ID using an updater function.
|
|
51
|
+
* @param messageId The ID of the message to update.
|
|
52
|
+
* @param updater A function that receives the current message and returns the updated message.
|
|
53
|
+
* @returns true if the message was found and updated, false otherwise.
|
|
54
|
+
*/
|
|
55
|
+
updateMessage(messageId: string, updater: (message: Message) => Message): boolean;
|
|
56
|
+
abort(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Returns a promise that resolves when the current prompt completes.
|
|
59
|
+
* Returns immediately resolved promise if no prompt is running.
|
|
60
|
+
*/
|
|
61
|
+
waitForIdle(): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Clear all messages and state. Aborts any running prompt.
|
|
64
|
+
*/
|
|
65
|
+
reset(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Internal cleanup after agent loop completes (success, error, or abort).
|
|
68
|
+
* Always called in finally block to ensure consistent state.
|
|
69
|
+
*/
|
|
70
|
+
private _cleanup;
|
|
71
|
+
/**
|
|
72
|
+
* Append custom message to messages.
|
|
73
|
+
* Custom messages are inserted after running prompt resolves
|
|
74
|
+
*/
|
|
75
|
+
addCustomMessage(message: Record<string, any>): Promise<void>;
|
|
76
|
+
prompt(input: string, attachments?: Attachment[]): Promise<Message[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Continue from the current context without adding a new user message.
|
|
79
|
+
* Used for retry after overflow recovery when context already has user message or tool results.
|
|
80
|
+
*/
|
|
81
|
+
continue(): Promise<Message[]>;
|
|
82
|
+
/**
|
|
83
|
+
* Prepare for running the agent loop.
|
|
84
|
+
* Returns the config, transformed messages, and abort signal.
|
|
85
|
+
*/
|
|
86
|
+
private _prepareRun;
|
|
87
|
+
/**
|
|
88
|
+
* Internal: Run the agent loop with a new user message.
|
|
89
|
+
*/
|
|
90
|
+
private _runAgentLoop;
|
|
91
|
+
private _runAgentLoopContinue;
|
|
92
|
+
/**
|
|
93
|
+
* Create callbacks for AgentRunner to interact with Conversation state.
|
|
94
|
+
*/
|
|
95
|
+
private _createRunnerCallbacks;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../src/agent/conversation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAiB,OAAO,EAAqB,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,UAAU,EAAmB,UAAU,EAAa,UAAU,EAAE,QAAQ,EAAiB,MAAM,YAAY,CAAC;AAErH,OAAO,EAAE,SAAS,EAAoB,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,WAAW,EAA4C,MAAM,aAAa,CAAC;AAEpF,MAAM,WAAW,YAAY;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAEnC,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IAE7E,SAAS,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;IAEpC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAyBD,qBAAa,YAAY;IACxB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,kBAAkB,CAA0D;IACpF,OAAO,CAAC,YAAY,CAAqC;IACzD,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,oBAAoB,CAAC,CAAa;IAC1C,OAAO,CAAC,sBAAsB,CAAiB;gBAEnC,IAAI,GAAE,YAAiB;IAiBnC,IAAI,KAAK,IAAI,UAAU,CAEtB;IAED,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAKlD,OAAO,CAAC,IAAI;IAMZ,yBAAyB,CAAC,MAAM,EAAE,OAAO;IAOzC,eAAe,CAAC,CAAC,EAAE,MAAM;IAIzB,WAAW,CAAC,IAAI,SAAS,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC;IAItD,YAAY,CAAC,IAAI,EAAE,KAAK,GAAG,eAAe;IAI1C,YAAY,IAAI,KAAK,GAAG,eAAe;IAIvC,QAAQ,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK;IAIpC,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE;IAI7B,aAAa,CAAC,CAAC,EAAE,OAAO;IAIxB,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE;IAItB,YAAY,CAAC,CAAC,EAAE,OAAO;IAS7B,iBAAiB;IAIjB,aAAa;IAIb;;OAEG;IACH,cAAc;IAId;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAUzC;;;;;OAKG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO;IAYjF,KAAK;IAIL;;;OAGG;IACH,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,KAAK;IAkBL;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAShB;;;OAGG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAgB7C,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAgB3E;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAepC;;;OAGG;YACW,WAAW;IAuCzB;;OAEG;YACW,aAAa;YAqBb,qBAAqB;IA2BnC;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAQ9B"}
|