@falai/agent 1.2.0 → 1.2.1

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 (72) hide show
  1. package/dist/cjs/core/ResponseEngine.d.ts +0 -2
  2. package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
  3. package/dist/cjs/core/ResponseEngine.js +2 -6
  4. package/dist/cjs/core/ResponseEngine.js.map +1 -1
  5. package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
  6. package/dist/cjs/core/ResponseModal.js +45 -54
  7. package/dist/cjs/core/ResponseModal.js.map +1 -1
  8. package/dist/cjs/core/ResponsePipeline.d.ts +2 -1
  9. package/dist/cjs/core/ResponsePipeline.d.ts.map +1 -1
  10. package/dist/cjs/core/ResponsePipeline.js +17 -19
  11. package/dist/cjs/core/ResponsePipeline.js.map +1 -1
  12. package/dist/cjs/core/RoutingEngine.js +2 -2
  13. package/dist/cjs/core/RoutingEngine.js.map +1 -1
  14. package/dist/cjs/providers/AnthropicProvider.d.ts +7 -0
  15. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  16. package/dist/cjs/providers/AnthropicProvider.js +101 -12
  17. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  18. package/dist/cjs/providers/GeminiProvider.d.ts +7 -0
  19. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  20. package/dist/cjs/providers/GeminiProvider.js +81 -2
  21. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  22. package/dist/cjs/providers/OpenAIProvider.d.ts +5 -0
  23. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  24. package/dist/cjs/providers/OpenAIProvider.js +51 -12
  25. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  26. package/dist/cjs/providers/OpenRouterProvider.d.ts +5 -0
  27. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  28. package/dist/cjs/providers/OpenRouterProvider.js +50 -12
  29. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  30. package/dist/cjs/types/ai.d.ts +2 -2
  31. package/dist/cjs/types/ai.d.ts.map +1 -1
  32. package/dist/core/ResponseEngine.d.ts +0 -2
  33. package/dist/core/ResponseEngine.d.ts.map +1 -1
  34. package/dist/core/ResponseEngine.js +2 -6
  35. package/dist/core/ResponseEngine.js.map +1 -1
  36. package/dist/core/ResponseModal.d.ts.map +1 -1
  37. package/dist/core/ResponseModal.js +46 -55
  38. package/dist/core/ResponseModal.js.map +1 -1
  39. package/dist/core/ResponsePipeline.d.ts +2 -1
  40. package/dist/core/ResponsePipeline.d.ts.map +1 -1
  41. package/dist/core/ResponsePipeline.js +18 -20
  42. package/dist/core/ResponsePipeline.js.map +1 -1
  43. package/dist/core/RoutingEngine.js +3 -3
  44. package/dist/core/RoutingEngine.js.map +1 -1
  45. package/dist/providers/AnthropicProvider.d.ts +7 -0
  46. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  47. package/dist/providers/AnthropicProvider.js +101 -12
  48. package/dist/providers/AnthropicProvider.js.map +1 -1
  49. package/dist/providers/GeminiProvider.d.ts +7 -0
  50. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  51. package/dist/providers/GeminiProvider.js +81 -2
  52. package/dist/providers/GeminiProvider.js.map +1 -1
  53. package/dist/providers/OpenAIProvider.d.ts +5 -0
  54. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  55. package/dist/providers/OpenAIProvider.js +51 -12
  56. package/dist/providers/OpenAIProvider.js.map +1 -1
  57. package/dist/providers/OpenRouterProvider.d.ts +5 -0
  58. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  59. package/dist/providers/OpenRouterProvider.js +50 -12
  60. package/dist/providers/OpenRouterProvider.js.map +1 -1
  61. package/dist/types/ai.d.ts +2 -2
  62. package/dist/types/ai.d.ts.map +1 -1
  63. package/package.json +1 -1
  64. package/src/core/ResponseEngine.ts +2 -9
  65. package/src/core/ResponseModal.ts +56 -67
  66. package/src/core/ResponsePipeline.ts +22 -22
  67. package/src/core/RoutingEngine.ts +3 -3
  68. package/src/providers/AnthropicProvider.ts +110 -12
  69. package/src/providers/GeminiProvider.ts +91 -2
  70. package/src/providers/OpenAIProvider.ts +56 -12
  71. package/src/providers/OpenRouterProvider.ts +55 -12
  72. package/src/types/ai.ts +2 -2
@@ -19,6 +19,7 @@ import type {
19
19
  AgentStructuredResponse,
20
20
  StructuredSchema,
21
21
  } from "../types";
22
+ import type { HistoryItem } from "../types/history";
22
23
  import { withTimeoutAndRetry } from "../utils/retry";
23
24
  import { tryParseJSONResponse } from "../utils/json";
24
25
  import { logger } from "../utils/logger";
@@ -152,6 +153,62 @@ export class GeminiProvider implements AiProvider {
152
153
  };
153
154
  }
154
155
 
156
+ /**
157
+ * Build Gemini-formatted contents from HistoryItem[] array.
158
+ * Gemini uses "user"/"model" roles (not "assistant").
159
+ * System messages are extracted separately for systemInstruction.
160
+ * Tool results map to functionResponse parts, tool calls to functionCall parts.
161
+ */
162
+ private buildGeminiContents(history: HistoryItem[]): {
163
+ contents: Array<{ role: string; parts: Array<Record<string, unknown>> }>;
164
+ systemInstructions: string[];
165
+ } {
166
+ const contents: Array<{ role: string; parts: Array<Record<string, unknown>> }> = [];
167
+ const systemInstructions: string[] = [];
168
+
169
+ for (const item of history) {
170
+ switch (item.role) {
171
+ case "system":
172
+ systemInstructions.push(item.content);
173
+ break;
174
+ case "user":
175
+ contents.push({ role: "user", parts: [{ text: item.content }] });
176
+ break;
177
+ case "assistant":
178
+ if (item.tool_calls && item.tool_calls.length > 0) {
179
+ const parts: Array<Record<string, unknown>> = [];
180
+ if (item.content) {
181
+ parts.push({ text: item.content });
182
+ }
183
+ for (const tc of item.tool_calls) {
184
+ parts.push({
185
+ functionCall: { name: tc.name, args: tc.arguments },
186
+ });
187
+ }
188
+ contents.push({ role: "model", parts });
189
+ } else {
190
+ contents.push({ role: "model", parts: [{ text: item.content || "" }] });
191
+ }
192
+ break;
193
+ case "tool":
194
+ contents.push({
195
+ role: "user",
196
+ parts: [
197
+ {
198
+ functionResponse: {
199
+ name: item.name,
200
+ response: typeof item.content === "object" ? item.content : { result: item.content },
201
+ },
202
+ },
203
+ ],
204
+ });
205
+ break;
206
+ }
207
+ }
208
+
209
+ return { contents, systemInstructions };
210
+ }
211
+
155
212
  /**
156
213
  * Convert tool parameter schemas (JSON Schema) to Gemini's Schema format.
157
214
  * Gemini's FunctionDeclaration.parameters expects its own Schema type,
@@ -430,9 +487,25 @@ export class GeminiProvider implements AiProvider {
430
487
 
431
488
  let response: GenerateContentResponse;
432
489
  try {
490
+ // Build contents from history
491
+ const { contents: historyContents, systemInstructions } = this.buildGeminiContents(input.history);
492
+
493
+ // Append the current prompt as the final user content
494
+ historyContents.push({ role: "user", parts: [{ text: input.prompt }] });
495
+
496
+ // Set system instruction from history if present
497
+ if (systemInstructions.length > 0) {
498
+ const existingSystem = configOverride.systemInstruction;
499
+ if (typeof existingSystem === "string") {
500
+ configOverride.systemInstruction = `${existingSystem}\n\n${systemInstructions.join("\n\n")}`;
501
+ } else {
502
+ configOverride.systemInstruction = systemInstructions.join("\n\n");
503
+ }
504
+ }
505
+
433
506
  response = await this.genAI.models.generateContent({
434
507
  model,
435
- contents: input.prompt,
508
+ contents: historyContents,
436
509
  config: {
437
510
  ...configOverride,
438
511
  ...(input.signal ? { abortSignal: input.signal } : {}),
@@ -620,9 +693,25 @@ export class GeminiProvider implements AiProvider {
620
693
 
621
694
  let stream;
622
695
  try {
696
+ // Build contents from history
697
+ const { contents: historyContents, systemInstructions } = this.buildGeminiContents(input.history);
698
+
699
+ // Append the current prompt as the final user content
700
+ historyContents.push({ role: "user", parts: [{ text: input.prompt }] });
701
+
702
+ // Set system instruction from history if present
703
+ if (systemInstructions.length > 0) {
704
+ const existingSystem = configOverride.systemInstruction;
705
+ if (typeof existingSystem === "string") {
706
+ configOverride.systemInstruction = `${existingSystem}\n\n${systemInstructions.join("\n\n")}`;
707
+ } else {
708
+ configOverride.systemInstruction = systemInstructions.join("\n\n");
709
+ }
710
+ }
711
+
623
712
  stream = await this.genAI.models.generateContentStream({
624
713
  model,
625
- contents: input.prompt,
714
+ contents: historyContents,
626
715
  config: {
627
716
  ...configOverride,
628
717
  ...(input.signal ? { abortSignal: input.signal } : {}),
@@ -13,6 +13,7 @@ import type {
13
13
  AgentStructuredResponse,
14
14
  StructuredSchema,
15
15
  } from "../types";
16
+ import type { HistoryItem } from "../types/history";
16
17
  import { withTimeoutAndRetry, logger } from "../utils";
17
18
  import { FunctionParameters } from "openai/resources/shared.mjs";
18
19
 
@@ -161,6 +162,52 @@ export class OpenAIProvider implements AiProvider {
161
162
  };
162
163
  }
163
164
 
165
+ /**
166
+ * Build OpenAI-formatted messages from HistoryItem[] array.
167
+ * Maps directly to ChatCompletionMessageParam format.
168
+ */
169
+ private buildOpenAIMessages(history: HistoryItem[]): Array<unknown> {
170
+ const messages: Array<unknown> = [];
171
+
172
+ for (const item of history) {
173
+ switch (item.role) {
174
+ case "system":
175
+ messages.push({ role: "system", content: item.content });
176
+ break;
177
+ case "user":
178
+ messages.push({ role: "user", content: item.content });
179
+ break;
180
+ case "assistant":
181
+ if (item.tool_calls && item.tool_calls.length > 0) {
182
+ messages.push({
183
+ role: "assistant",
184
+ content: item.content || null,
185
+ tool_calls: item.tool_calls.map(tc => ({
186
+ id: tc.id,
187
+ type: "function",
188
+ function: {
189
+ name: tc.name,
190
+ arguments: JSON.stringify(tc.arguments),
191
+ },
192
+ })),
193
+ });
194
+ } else {
195
+ messages.push({ role: "assistant", content: item.content || "" });
196
+ }
197
+ break;
198
+ case "tool":
199
+ messages.push({
200
+ role: "tool",
201
+ tool_call_id: item.tool_call_id,
202
+ content: typeof item.content === "string" ? item.content : JSON.stringify(item.content),
203
+ });
204
+ break;
205
+ }
206
+ }
207
+
208
+ return messages;
209
+ }
210
+
164
211
  /**
165
212
  * Adapt common schema format to OpenAI's format.
166
213
  * OpenAI uses standard JSON Schema, so this is mostly a passthrough.
@@ -265,14 +312,12 @@ export class OpenAIProvider implements AiProvider {
265
312
  input: GenerateMessageInput<TContext>
266
313
  ): Promise<GenerateMessageOutput<TStructured>> {
267
314
  const operation = async (): Promise<GenerateMessageOutput> => {
315
+ const historyMessages = this.buildOpenAIMessages(input.history);
316
+ historyMessages.push({ role: "user", content: input.prompt });
317
+
268
318
  const params: ChatCompletionCreateParamsNonStreaming = {
269
319
  model,
270
- messages: [
271
- {
272
- role: "user",
273
- content: input.prompt,
274
- },
275
- ],
320
+ messages: historyMessages as ChatCompletionCreateParamsNonStreaming["messages"],
276
321
  ...this.config,
277
322
  };
278
323
 
@@ -468,15 +513,14 @@ export class OpenAIProvider implements AiProvider {
468
513
  model: string,
469
514
  input: GenerateMessageInput<TContext>
470
515
  ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
516
+ // Build messages from history and append prompt as final user message
517
+ const historyMessages = this.buildOpenAIMessages(input.history);
518
+ historyMessages.push({ role: "user" as const, content: input.prompt });
519
+
471
520
  const params = {
472
521
  ...this.config,
473
522
  model,
474
- messages: [
475
- {
476
- role: "user" as const,
477
- content: input.prompt,
478
- },
479
- ],
523
+ messages: historyMessages as ChatCompletionCreateParamsNonStreaming["messages"],
480
524
  stream: true as const,
481
525
  };
482
526
 
@@ -15,6 +15,7 @@ import type {
15
15
  GenerateMessageStreamChunk,
16
16
  StructuredSchema,
17
17
  } from "../types";
18
+ import type { HistoryItem } from "../types/history";
18
19
  import { withTimeoutAndRetry, logger } from "../utils";
19
20
 
20
21
  const DEFAULT_RETRY_CONFIG = {
@@ -171,6 +172,52 @@ export class OpenRouterProvider implements AiProvider {
171
172
  };
172
173
  }
173
174
 
175
+ /**
176
+ * Build OpenRouter-formatted messages from HistoryItem[] array.
177
+ * OpenRouter uses OpenAI-compatible format.
178
+ */
179
+ private buildOpenRouterMessages(history: HistoryItem[]): Array<unknown> {
180
+ const messages: Array<unknown> = [];
181
+
182
+ for (const item of history) {
183
+ switch (item.role) {
184
+ case "system":
185
+ messages.push({ role: "system", content: item.content });
186
+ break;
187
+ case "user":
188
+ messages.push({ role: "user", content: item.content });
189
+ break;
190
+ case "assistant":
191
+ if (item.tool_calls && item.tool_calls.length > 0) {
192
+ messages.push({
193
+ role: "assistant",
194
+ content: item.content || null,
195
+ tool_calls: item.tool_calls.map(tc => ({
196
+ id: tc.id,
197
+ type: "function",
198
+ function: {
199
+ name: tc.name,
200
+ arguments: JSON.stringify(tc.arguments),
201
+ },
202
+ })),
203
+ });
204
+ } else {
205
+ messages.push({ role: "assistant", content: item.content || "" });
206
+ }
207
+ break;
208
+ case "tool":
209
+ messages.push({
210
+ role: "tool",
211
+ tool_call_id: item.tool_call_id,
212
+ content: typeof item.content === "string" ? item.content : JSON.stringify(item.content),
213
+ });
214
+ break;
215
+ }
216
+ }
217
+
218
+ return messages;
219
+ }
220
+
174
221
  /**
175
222
  * Adapt common schema format to OpenRouter's format.
176
223
  * OpenRouter is OpenAI-compatible and uses standard JSON Schema.
@@ -272,14 +319,12 @@ export class OpenRouterProvider implements AiProvider {
272
319
  input: GenerateMessageInput<TContext>
273
320
  ): Promise<GenerateMessageOutput<TStructured>> {
274
321
  const operation = async (): Promise<GenerateMessageOutput> => {
322
+ const historyMessages = this.buildOpenRouterMessages(input.history);
323
+ historyMessages.push({ role: "user", content: input.prompt });
324
+
275
325
  const params: ChatCompletionCreateParamsNonStreaming = {
276
326
  model,
277
- messages: [
278
- {
279
- role: "user",
280
- content: input.prompt,
281
- },
282
- ],
327
+ messages: historyMessages as ChatCompletionCreateParamsNonStreaming["messages"],
283
328
  ...this.config,
284
329
  };
285
330
 
@@ -471,15 +516,13 @@ export class OpenRouterProvider implements AiProvider {
471
516
  model: string,
472
517
  input: GenerateMessageInput<TContext>
473
518
  ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
519
+ const historyMessages = this.buildOpenRouterMessages(input.history);
520
+ historyMessages.push({ role: "user" as const, content: input.prompt });
521
+
474
522
  const params = {
475
523
  ...this.config,
476
524
  model,
477
- messages: [
478
- {
479
- role: "user" as const,
480
- content: input.prompt,
481
- },
482
- ],
525
+ messages: historyMessages as ChatCompletionCreateParamsNonStreaming["messages"],
483
526
  stream: true as const,
484
527
  };
485
528
 
package/src/types/ai.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * AI provider strategy types
3
3
  */
4
4
 
5
- import type { Event } from "./history";
5
+ import type { HistoryItem } from "./history";
6
6
 
7
7
  /**
8
8
  * Reasoning/thinking configuration for AI models
@@ -36,7 +36,7 @@ export interface GenerateMessageInput<TContext = unknown> {
36
36
  /** The constructed prompt */
37
37
  prompt: string;
38
38
  /** Interaction history */
39
- history: Event[];
39
+ history: HistoryItem[];
40
40
  /** Context data */
41
41
  context: TContext;
42
42
  /** Tools available for AI to call during this interaction */