@ank1015/providers 0.0.1 → 0.0.2

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.
Files changed (169) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -383
  3. package/dist/agent/conversation.d.ts +97 -0
  4. package/dist/agent/conversation.d.ts.map +1 -0
  5. package/dist/agent/conversation.js +328 -0
  6. package/dist/agent/conversation.js.map +1 -0
  7. package/dist/agent/runner.d.ts +37 -0
  8. package/dist/agent/runner.d.ts.map +1 -0
  9. package/dist/agent/runner.js +169 -0
  10. package/dist/agent/runner.js.map +1 -0
  11. package/dist/agent/tools/calculate.d.ts +15 -0
  12. package/dist/agent/tools/calculate.d.ts.map +1 -0
  13. package/dist/agent/tools/calculate.js +23 -0
  14. package/dist/agent/tools/calculate.js.map +1 -0
  15. package/dist/agent/tools/get-current-time.d.ts +15 -0
  16. package/dist/agent/tools/get-current-time.d.ts.map +1 -0
  17. package/dist/agent/tools/get-current-time.js +38 -0
  18. package/dist/agent/tools/get-current-time.js.map +1 -0
  19. package/dist/agent/tools/index.d.ts +3 -0
  20. package/dist/agent/tools/index.d.ts.map +1 -0
  21. package/dist/agent/tools/index.js +3 -0
  22. package/dist/agent/tools/index.js.map +1 -0
  23. package/dist/agent/types.d.ts +53 -31
  24. package/dist/agent/types.d.ts.map +1 -1
  25. package/dist/agent/types.js +1 -2
  26. package/dist/agent/utils.d.ts +14 -0
  27. package/dist/agent/utils.d.ts.map +1 -0
  28. package/dist/agent/utils.js +59 -0
  29. package/dist/agent/utils.js.map +1 -0
  30. package/dist/index.d.ts +16 -9
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +16 -28
  33. package/dist/index.js.map +1 -1
  34. package/dist/llm.d.ts +15 -0
  35. package/dist/llm.d.ts.map +1 -0
  36. package/dist/llm.js +92 -0
  37. package/dist/llm.js.map +1 -0
  38. package/dist/models.d.ts +8 -1
  39. package/dist/models.d.ts.map +1 -1
  40. package/dist/models.generated.d.ts +25 -112
  41. package/dist/models.generated.d.ts.map +1 -1
  42. package/dist/models.generated.js +72 -227
  43. package/dist/models.generated.js.map +1 -1
  44. package/dist/models.js +30 -32
  45. package/dist/models.js.map +1 -1
  46. package/dist/providers/google/complete.d.ts +3 -0
  47. package/dist/providers/google/complete.d.ts.map +1 -0
  48. package/dist/providers/google/complete.js +53 -0
  49. package/dist/providers/google/complete.js.map +1 -0
  50. package/dist/providers/google/index.d.ts +6 -0
  51. package/dist/providers/google/index.d.ts.map +1 -0
  52. package/dist/providers/google/index.js +6 -0
  53. package/dist/providers/google/index.js.map +1 -0
  54. package/dist/providers/google/stream.d.ts +3 -0
  55. package/dist/providers/google/stream.d.ts.map +1 -0
  56. package/dist/providers/{google.js → google/stream.js} +67 -231
  57. package/dist/providers/google/stream.js.map +1 -0
  58. package/dist/providers/google/types.d.ts +8 -0
  59. package/dist/providers/google/types.d.ts.map +1 -0
  60. package/dist/providers/google/types.js +2 -0
  61. package/dist/providers/google/types.js.map +1 -0
  62. package/dist/providers/google/utils.d.ts +30 -0
  63. package/dist/providers/google/utils.d.ts.map +1 -0
  64. package/dist/providers/google/utils.js +354 -0
  65. package/dist/providers/google/utils.js.map +1 -0
  66. package/dist/providers/openai/complete.d.ts +3 -0
  67. package/dist/providers/openai/complete.d.ts.map +1 -0
  68. package/dist/providers/openai/complete.js +57 -0
  69. package/dist/providers/openai/complete.js.map +1 -0
  70. package/dist/providers/openai/index.d.ts +4 -0
  71. package/dist/providers/openai/index.d.ts.map +1 -0
  72. package/dist/providers/openai/index.js +4 -0
  73. package/dist/providers/openai/index.js.map +1 -0
  74. package/dist/providers/openai/stream.d.ts +3 -0
  75. package/dist/providers/openai/stream.d.ts.map +1 -0
  76. package/dist/providers/{openai.js → openai/stream.js} +74 -152
  77. package/dist/providers/openai/stream.js.map +1 -0
  78. package/dist/providers/openai/types.d.ts +8 -0
  79. package/dist/providers/openai/types.d.ts.map +1 -0
  80. package/dist/providers/openai/types.js +2 -0
  81. package/dist/providers/openai/types.js.map +1 -0
  82. package/dist/providers/openai/utils.d.ts +13 -0
  83. package/dist/providers/openai/utils.d.ts.map +1 -0
  84. package/dist/providers/openai/utils.js +285 -0
  85. package/dist/providers/openai/utils.js.map +1 -0
  86. package/dist/types.d.ts +95 -87
  87. package/dist/types.d.ts.map +1 -1
  88. package/dist/types.js +1 -9
  89. package/dist/types.js.map +1 -1
  90. package/dist/utils/event-stream.d.ts +2 -2
  91. package/dist/utils/event-stream.d.ts.map +1 -1
  92. package/dist/utils/event-stream.js +2 -7
  93. package/dist/utils/event-stream.js.map +1 -1
  94. package/dist/utils/json-parse.js +3 -6
  95. package/dist/utils/json-parse.js.map +1 -1
  96. package/dist/utils/overflow.d.ts +51 -0
  97. package/dist/utils/overflow.d.ts.map +1 -0
  98. package/dist/utils/overflow.js +106 -0
  99. package/dist/utils/overflow.js.map +1 -0
  100. package/dist/utils/sanitize-unicode.js +1 -4
  101. package/dist/utils/sanitize-unicode.js.map +1 -1
  102. package/dist/utils/uuid.d.ts +6 -0
  103. package/dist/utils/uuid.d.ts.map +1 -0
  104. package/dist/utils/uuid.js +9 -0
  105. package/dist/utils/uuid.js.map +1 -0
  106. package/dist/utils/validation.d.ts +10 -3
  107. package/dist/utils/validation.d.ts.map +1 -1
  108. package/dist/utils/validation.js +20 -12
  109. package/dist/utils/validation.js.map +1 -1
  110. package/package.json +45 -8
  111. package/biome.json +0 -43
  112. package/dist/agent/agent-loop.d.ts +0 -5
  113. package/dist/agent/agent-loop.d.ts.map +0 -1
  114. package/dist/agent/agent-loop.js +0 -219
  115. package/dist/agent/agent-loop.js.map +0 -1
  116. package/dist/providers/convert.d.ts +0 -6
  117. package/dist/providers/convert.d.ts.map +0 -1
  118. package/dist/providers/convert.js +0 -207
  119. package/dist/providers/convert.js.map +0 -1
  120. package/dist/providers/google.d.ts +0 -26
  121. package/dist/providers/google.d.ts.map +0 -1
  122. package/dist/providers/google.js.map +0 -1
  123. package/dist/providers/openai.d.ts +0 -17
  124. package/dist/providers/openai.d.ts.map +0 -1
  125. package/dist/providers/openai.js.map +0 -1
  126. package/dist/stream.d.ts +0 -4
  127. package/dist/stream.d.ts.map +0 -1
  128. package/dist/stream.js +0 -40
  129. package/dist/stream.js.map +0 -1
  130. package/dist/test-google-agent-loop.d.ts +0 -2
  131. package/dist/test-google-agent-loop.d.ts.map +0 -1
  132. package/dist/test-google-agent-loop.js +0 -186
  133. package/dist/test-google-agent-loop.js.map +0 -1
  134. package/dist/test-google.d.ts +0 -2
  135. package/dist/test-google.d.ts.map +0 -1
  136. package/dist/test-google.js +0 -41
  137. package/dist/test-google.js.map +0 -1
  138. package/src/agent/agent-loop.ts +0 -275
  139. package/src/agent/types.ts +0 -80
  140. package/src/index.ts +0 -72
  141. package/src/models.generated.ts +0 -314
  142. package/src/models.ts +0 -45
  143. package/src/providers/convert.ts +0 -222
  144. package/src/providers/google.ts +0 -496
  145. package/src/providers/openai.ts +0 -437
  146. package/src/stream.ts +0 -60
  147. package/src/types.ts +0 -198
  148. package/src/utils/event-stream.ts +0 -60
  149. package/src/utils/json-parse.ts +0 -28
  150. package/src/utils/sanitize-unicode.ts +0 -25
  151. package/src/utils/validation.ts +0 -69
  152. package/test/core/agent-loop.test.ts +0 -958
  153. package/test/core/stream.test.ts +0 -409
  154. package/test/data/red-circle.png +0 -0
  155. package/test/data/superintelligentwill.pdf +0 -0
  156. package/test/edge-cases/general.test.ts +0 -565
  157. package/test/integration/e2e.test.ts +0 -530
  158. package/test/models/cost.test.ts +0 -499
  159. package/test/models/registry.test.ts +0 -298
  160. package/test/providers/convert.test.ts +0 -846
  161. package/test/providers/google-schema.test.ts +0 -666
  162. package/test/providers/google-stream.test.ts +0 -369
  163. package/test/providers/openai-stream.test.ts +0 -251
  164. package/test/utils/event-stream.test.ts +0 -289
  165. package/test/utils/json-parse.test.ts +0 -344
  166. package/test/utils/sanitize-unicode.test.ts +0 -329
  167. package/test/utils/validation.test.ts +0 -614
  168. package/tsconfig.json +0 -21
  169. 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
- This documentation is written by claude. Most of the coding patterns are taken and inspired from [PI-mono](https://github.com/badlogic/pi-mono/tree/main)
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
- - **🤖 Agent Loop**: Multi-turn conversations with automatic tool execution
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
- ### Basic Streaming
36
+ Here's how to create a simple agent that can perform calculations.
52
37
 
53
38
  ```typescript
54
- import { stream, MODELS } from "@ank1015/providers";
55
-
56
- const context = {
57
- systemPrompt: "You are a helpful assistant.",
58
- messages: [
59
- {
60
- role: "user",
61
- content: [{ type: "text", content: "Hello!" }],
62
- timestamp: Date.now(),
63
- },
64
- ],
65
- };
66
-
67
- const response = stream(MODELS.OPENAI_GPT5_MINI, context);
68
-
69
- for await (const event of response) {
70
- if (event.type === "text_delta") {
71
- process.stdout.write(event.delta);
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
- ### Agent Loop with Tools
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
- const prompt = {
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
- const config = {
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 { stream, MODELS } from "@ank1015/providers";
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
- // Both return the same standardized stream format
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
- ### Cost Tracking
78
+ // Switch to OpenAI GPT-5.2 (Example Model ID from registry)
79
+ const openAIModel = getModel('openai', 'gpt-5.2');
176
80
 
177
- ```typescript
178
- for await (const event of response) {
179
- if (event.type === "done") {
180
- const { usage } = event.message;
181
- console.log(`Tokens: ${usage.totalTokens}`);
182
- console.log(`Cost: $${usage.cost.total.toFixed(4)}`);
183
- console.log(`Input: ${usage.input} ($${usage.cost.input.toFixed(4)})`);
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
- ## Architecture
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
- The library provides 21 event types for granular control:
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
- type AssistantMessageEvent =
215
- | { type: "start"; partial: AssistantMessage }
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
- Helper for creating tools with better type inference:
309
- ```typescript
310
- const tool = defineTool({
311
- name: "search",
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
- Calculate costs based on token usage:
322
- ```typescript
323
- import { calculateCost, MODELS } from "@ank1015/providers";
324
-
325
- const usage = {
326
- input: 1000,
327
- output: 500,
328
- cacheRead: 200,
329
- cacheWrite: 0,
330
- totalTokens: 1700,
331
- cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
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
- calculateCost(MODELS.OPENAI_GPT5_MINI, usage);
335
- console.log(usage.cost.total); // Calculated cost in USD
124
+ // 4. Register with Conversation
125
+ conversation.setTools([getWeatherTool]);
336
126
  ```
337
127
 
338
- ## Provider-Specific Features
128
+ ### 3. Streaming Events
339
129
 
340
- ### OpenAI
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
- stream(MODELS.OPENAI_GPT5_MINI, context, {
344
- reasoning: {
345
- effort: "low" | "medium" | "high",
346
- summaryStyle: "concise" | "detailed",
347
- },
348
- parallelToolCalls: true,
349
- prompt_cache_key: "unique-cache-key",
350
- promptCacheRetention: "in-memory" | "24h",
351
- maxOutputTokens: 4096,
352
- temperature: 0.7,
353
- });
354
- ```
355
-
356
- ### Google
357
-
358
- ```typescript
359
- stream(MODELS.GOOGLE_GEMINI_2_5_FLASH, context, {
360
- thinkingConfig: {
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
- ## Design Principles
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
- Contributions are welcome! Please feel free to submit issues or pull requests.
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"}