@falai/agent 0.1.0 → 0.1.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 (60) hide show
  1. package/dist/core/Agent.d.ts +18 -6
  2. package/dist/core/Agent.d.ts.map +1 -1
  3. package/dist/core/Agent.js +45 -6
  4. package/dist/core/Agent.js.map +1 -1
  5. package/dist/core/Events.d.ts +2 -2
  6. package/dist/core/Events.d.ts.map +1 -1
  7. package/dist/core/Events.js +1 -1
  8. package/dist/core/Events.js.map +1 -1
  9. package/dist/core/Observation.d.ts +3 -3
  10. package/dist/core/Observation.d.ts.map +1 -1
  11. package/dist/core/PromptBuilder.d.ts +8 -4
  12. package/dist/core/PromptBuilder.d.ts.map +1 -1
  13. package/dist/core/PromptBuilder.js +35 -3
  14. package/dist/core/PromptBuilder.js.map +1 -1
  15. package/dist/core/Route.d.ts +3 -3
  16. package/dist/core/Route.d.ts.map +1 -1
  17. package/dist/core/Route.js +1 -1
  18. package/dist/core/Route.js.map +1 -1
  19. package/dist/core/State.d.ts +3 -3
  20. package/dist/core/State.d.ts.map +1 -1
  21. package/dist/core/State.js +2 -2
  22. package/dist/core/State.js.map +1 -1
  23. package/dist/core/Tool.d.ts +1 -1
  24. package/dist/core/Tool.d.ts.map +1 -1
  25. package/dist/core/Transition.d.ts +2 -2
  26. package/dist/core/Transition.d.ts.map +1 -1
  27. package/dist/index.d.ts +10 -10
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +3 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/providers/GeminiProvider.d.ts +1 -1
  32. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  33. package/dist/providers/GeminiProvider.js +19 -2
  34. package/dist/providers/GeminiProvider.js.map +1 -1
  35. package/dist/providers/OpenAIProvider.d.ts +1 -1
  36. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  37. package/dist/providers/OpenAIProvider.js +77 -1
  38. package/dist/providers/OpenAIProvider.js.map +1 -1
  39. package/dist/providers/OpenRouterProvider.d.ts +1 -1
  40. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  41. package/dist/providers/OpenRouterProvider.js +77 -1
  42. package/dist/providers/OpenRouterProvider.js.map +1 -1
  43. package/dist/types/ai.d.ts +24 -0
  44. package/dist/types/ai.d.ts.map +1 -1
  45. package/docs/API_REFERENCE.md +71 -2
  46. package/docs/CONTRIBUTING.md +2 -2
  47. package/package.json +1 -1
  48. package/src/core/Agent.ts +66 -10
  49. package/src/core/Events.ts +2 -2
  50. package/src/core/Observation.ts +3 -3
  51. package/src/core/PromptBuilder.ts +45 -6
  52. package/src/core/Route.ts +3 -3
  53. package/src/core/State.ts +9 -5
  54. package/src/core/Tool.ts +1 -1
  55. package/src/core/Transition.ts +2 -2
  56. package/src/index.ts +11 -10
  57. package/src/providers/GeminiProvider.ts +22 -3
  58. package/src/providers/OpenAIProvider.ts +86 -2
  59. package/src/providers/OpenRouterProvider.ts +86 -2
  60. package/src/types/ai.ts +25 -0
@@ -5,9 +5,9 @@
5
5
  import type {
6
6
  Observation as IObservation,
7
7
  ObservationOptions,
8
- } from "@/types/observation";
9
- import type { RouteRef } from "@/types/route";
10
- import type { Route } from "@/core/Route";
8
+ } from "../types/observation";
9
+ import type { RouteRef } from "../types/route";
10
+ import type { Route } from "./Route";
11
11
 
12
12
  let observationIdCounter = 0;
13
13
 
@@ -2,13 +2,13 @@
2
2
  * Prompt construction and management
3
3
  */
4
4
 
5
- import type { Event, EmittedEvent, MessageEventData } from "@/types/history";
6
- import { EventKind, EventSource } from "@/types/history";
7
- import type { Term, Capability, GuidelineMatch } from "@/types/agent";
8
- import type { PromptSection, ContextVariableValue } from "@/types/prompt";
9
- import { SectionStatus } from "@/types/prompt";
5
+ import type { Event, EmittedEvent, MessageEventData } from "../types/history";
6
+ import { EventKind, EventSource } from "../types/history";
7
+ import type { Term, Capability, GuidelineMatch } from "../types/agent";
8
+ import type { PromptSection, ContextVariableValue } from "../types/prompt";
9
+ import { SectionStatus } from "../types/prompt";
10
10
 
11
- import { adaptEvent } from "@/core/Events";
11
+ import { adaptEvent } from "./Events";
12
12
 
13
13
  /**
14
14
  * Built-in section identifiers
@@ -460,6 +460,45 @@ These routes represent different paths the conversation can take. Choose the mos
460
460
  return this;
461
461
  }
462
462
 
463
+ /**
464
+ * Add JSON response schema instructions
465
+ */
466
+ addJsonResponseSchema(): this {
467
+ const schema = {
468
+ message: "The actual message to send to the user",
469
+ route: "The title of the route you chose (or null if no specific route)",
470
+ state: "The current state within the route (or null if not in a route)",
471
+ toolCalls: [
472
+ {
473
+ toolName: "Name of the tool to call",
474
+ arguments: "Object with tool arguments",
475
+ },
476
+ ],
477
+ reasoning: "Optional: Your internal reasoning for this response",
478
+ };
479
+
480
+ this.addSection(
481
+ "json_response_format",
482
+ `IMPORTANT: You must respond with valid JSON in the following format:
483
+
484
+ \`\`\`json
485
+ ${JSON.stringify(schema, null, 2)}
486
+ \`\`\`
487
+
488
+ Instructions:
489
+ - "message": The actual message to send to the user (required)
490
+ - "route": If you chose a specific conversation route, provide its exact title. If not in a route, use null.
491
+ - "state": The current state within the chosen route. If not in a route or at initial state, use null.
492
+ - "toolCalls": If you need to call any tools, provide an array of tool calls. If no tools needed, use an empty array or omit.
493
+ - "reasoning": Optional field for your internal thinking process.
494
+
495
+ Your entire response must be valid JSON. Do not include any text before or after the JSON object.`,
496
+ {},
497
+ SectionStatus.ACTIVE
498
+ );
499
+ return this;
500
+ }
501
+
463
502
  // Helper methods
464
503
 
465
504
  private formatTemplate(
package/src/core/Route.ts CHANGED
@@ -2,10 +2,10 @@
2
2
  * Route (Journey) DSL implementation
3
3
  */
4
4
 
5
- import type { RouteOptions, RouteRef } from "@/types/route";
6
- import type { Guideline } from "@/types/agent";
5
+ import type { RouteOptions, RouteRef } from "../types/route";
6
+ import type { Guideline } from "../types/agent";
7
7
 
8
- import { State } from "@/core/State";
8
+ import { State } from "./State";
9
9
 
10
10
  let routeIdCounter = 0;
11
11
 
package/src/core/State.ts CHANGED
@@ -2,11 +2,15 @@
2
2
  * State in the route DSL
3
3
  */
4
4
 
5
- import type { StateRef, TransitionSpec, TransitionResult } from "@/types/route";
6
- import type { Guideline } from "@/types/agent";
7
-
8
- import { END_ROUTE } from "@/constants";
9
- import { Transition } from "@/core/Transition";
5
+ import type {
6
+ StateRef,
7
+ TransitionSpec,
8
+ TransitionResult,
9
+ } from "../types/route";
10
+ import type { Guideline } from "../types/agent";
11
+
12
+ import { END_ROUTE } from "../constants";
13
+ import { Transition } from "./Transition";
10
14
 
11
15
  let stateIdCounter = 0;
12
16
 
package/src/core/Tool.ts CHANGED
@@ -7,7 +7,7 @@ import type {
7
7
  ToolHandler,
8
8
  ToolRef,
9
9
  ToolResult,
10
- } from "@/types/tool";
10
+ } from "../types/tool";
11
11
 
12
12
  let toolIdCounter = 0;
13
13
 
@@ -2,8 +2,8 @@
2
2
  * Transition between states in the route DSL
3
3
  */
4
4
 
5
- import type { StateRef, TransitionSpec } from "@/types/route";
6
- import type { State } from "@/core/State";
5
+ import type { StateRef, TransitionSpec } from "../types/route";
6
+ import type { State } from "./State";
7
7
 
8
8
  /**
9
9
  * Represents a transition from one state to another
package/src/index.ts CHANGED
@@ -35,8 +35,8 @@ export type {
35
35
  Guideline,
36
36
  Capability,
37
37
  GuidelineMatch,
38
- } from "@/types/agent";
39
- export { CompositionMode } from "@/types/agent";
38
+ } from "./types/agent";
39
+ export { CompositionMode } from "./types/agent";
40
40
 
41
41
  export type {
42
42
  Event,
@@ -45,8 +45,8 @@ export type {
45
45
  ToolEventData,
46
46
  StatusEventData,
47
47
  Participant,
48
- } from "@/types/history";
49
- export { EventKind, EventSource } from "@/types/history";
48
+ } from "./types/history";
49
+ export { EventKind, EventSource } from "./types/history";
50
50
 
51
51
  export type {
52
52
  RouteRef,
@@ -54,30 +54,31 @@ export type {
54
54
  RouteOptions,
55
55
  TransitionSpec,
56
56
  TransitionResult,
57
- } from "@/types/route";
57
+ } from "./types/route";
58
58
 
59
59
  export type {
60
60
  ToolContext,
61
61
  ToolResult,
62
62
  ToolHandler,
63
63
  ToolRef,
64
- } from "@/types/tool";
64
+ } from "./types/tool";
65
65
 
66
66
  export type {
67
67
  AiProvider,
68
68
  GenerateMessageInput,
69
69
  GenerateMessageOutput,
70
+ AgentStructuredResponse,
70
71
  ReasoningConfig,
71
- } from "@/types/ai";
72
+ } from "./types/ai";
72
73
 
73
74
  export type {
74
75
  PromptSection,
75
76
  ContextVariable,
76
77
  ContextVariableValue,
77
- } from "@/types/prompt";
78
- export { SectionStatus } from "@/types/prompt";
78
+ } from "./types/prompt";
79
+ export { SectionStatus } from "./types/prompt";
79
80
 
80
81
  export type {
81
82
  Observation as IObservation,
82
83
  ObservationOptions,
83
- } from "@/types/observation";
84
+ } from "./types/observation";
@@ -13,8 +13,9 @@ import type {
13
13
  AiProvider,
14
14
  GenerateMessageInput,
15
15
  GenerateMessageOutput,
16
- } from "@/types/ai";
17
- import { withTimeoutAndRetry } from "@/utils/retry";
16
+ AgentStructuredResponse,
17
+ } from "../types/ai";
18
+ import { withTimeoutAndRetry } from "../utils/retry";
18
19
 
19
20
  const DEFAULT_RETRY_CONFIG = {
20
21
  timeout: 60000,
@@ -187,11 +188,17 @@ export class GeminiProvider implements AiProvider {
187
188
  input: GenerateMessageInput<TContext>
188
189
  ): Promise<GenerateMessageOutput> {
189
190
  const operation = async (): Promise<GenerateMessageOutput> => {
191
+ // Enable JSON mode if requested
192
+ const configOverride: Partial<GenerateContentConfig> = { ...this.config };
193
+ if (input.parameters?.jsonMode) {
194
+ configOverride.responseMimeType = "application/json";
195
+ }
196
+
190
197
  const response: GenerateContentResponse =
191
198
  await this.genAI.models.generateContent({
192
199
  model,
193
200
  contents: input.prompt,
194
- config: this.config,
201
+ config: configOverride,
195
202
  });
196
203
 
197
204
  const message = response.text;
@@ -199,6 +206,17 @@ export class GeminiProvider implements AiProvider {
199
206
  throw new Error("No response from Gemini");
200
207
  }
201
208
 
209
+ // Parse JSON response if JSON mode was enabled
210
+ let structured;
211
+ if (input.parameters?.jsonMode) {
212
+ try {
213
+ structured = JSON.parse(message) as AgentStructuredResponse;
214
+ } catch (error) {
215
+ console.warn("[GEMINI] Failed to parse JSON response:", error);
216
+ // Fall back to treating the message as plain text
217
+ }
218
+ }
219
+
202
220
  return {
203
221
  message,
204
222
  metadata: {
@@ -207,6 +225,7 @@ export class GeminiProvider implements AiProvider {
207
225
  promptTokens: response.usageMetadata?.promptTokenCount,
208
226
  completionTokens: response.usageMetadata?.candidatesTokenCount,
209
227
  },
228
+ structured,
210
229
  };
211
230
  };
212
231
 
@@ -9,8 +9,9 @@ import type {
9
9
  AiProvider,
10
10
  GenerateMessageInput,
11
11
  GenerateMessageOutput,
12
- } from "@/types/ai";
13
- import { withTimeoutAndRetry } from "@/utils/retry";
12
+ AgentStructuredResponse,
13
+ } from "../types/ai";
14
+ import { withTimeoutAndRetry } from "../utils/retry";
14
15
 
15
16
  const DEFAULT_RETRY_CONFIG = {
16
17
  timeout: 60000,
@@ -243,6 +244,89 @@ export class OpenAIProvider implements AiProvider {
243
244
  params.max_tokens = input.parameters.maxOutputTokens;
244
245
  }
245
246
 
247
+ // Use structured output API if JSON mode is enabled
248
+ if (input.parameters?.jsonMode) {
249
+ // Define the JSON schema for agent response
250
+ const agentResponseSchema = {
251
+ type: "object",
252
+ properties: {
253
+ message: {
254
+ type: "string",
255
+ description: "The actual message to send to the user",
256
+ },
257
+ route: {
258
+ type: ["string", "null"],
259
+ description:
260
+ "The title of the route chosen (or null if no specific route)",
261
+ },
262
+ state: {
263
+ type: ["string", "null"],
264
+ description:
265
+ "The current state within the route (or null if not in a route)",
266
+ },
267
+ toolCalls: {
268
+ type: "array",
269
+ items: {
270
+ type: "object",
271
+ properties: {
272
+ toolName: {
273
+ type: "string",
274
+ description: "Name of the tool to call",
275
+ },
276
+ arguments: {
277
+ type: "object",
278
+ description: "Arguments to pass to the tool",
279
+ },
280
+ },
281
+ required: ["toolName", "arguments"],
282
+ },
283
+ description: "Tool calls the agent wants to execute",
284
+ },
285
+ reasoning: {
286
+ type: "string",
287
+ description: "Optional: Internal reasoning for this response",
288
+ },
289
+ },
290
+ required: ["message"],
291
+ additionalProperties: false,
292
+ };
293
+
294
+ const response = await this.client.responses.parse({
295
+ model,
296
+ instructions: input.prompt,
297
+ input: "",
298
+ reasoning: {
299
+ effort: input.parameters?.reasoning?.effort || "low",
300
+ },
301
+ text: {
302
+ format: {
303
+ type: "json_schema",
304
+ name: "agentResponseSchema",
305
+ schema: agentResponseSchema,
306
+ },
307
+ },
308
+ });
309
+
310
+ if (!response.output_parsed) {
311
+ throw new Error("No parsed output returned from OpenAI");
312
+ }
313
+
314
+ const structured = response.output_parsed as AgentStructuredResponse;
315
+ const message = structured.message;
316
+
317
+ return {
318
+ message,
319
+ metadata: {
320
+ model: response.model,
321
+ tokensUsed: response.usage?.total_tokens,
322
+ promptTokens: response.usage?.input_tokens,
323
+ completionTokens: response.usage?.output_tokens,
324
+ },
325
+ structured,
326
+ };
327
+ }
328
+
329
+ // Fall back to regular chat completions API if JSON mode not enabled
246
330
  const response = await this.client.chat.completions.create(params);
247
331
 
248
332
  const message = response.choices[0]?.message?.content;
@@ -8,10 +8,11 @@ import type { ChatCompletionCreateParamsNonStreaming } from "openai/resources/ch
8
8
 
9
9
  import type {
10
10
  AiProvider,
11
+ AgentStructuredResponse,
11
12
  GenerateMessageInput,
12
13
  GenerateMessageOutput,
13
- } from "@/types/ai";
14
- import { withTimeoutAndRetry } from "@/utils/retry";
14
+ } from "../types/ai";
15
+ import { withTimeoutAndRetry } from "../utils/retry";
15
16
 
16
17
  const DEFAULT_RETRY_CONFIG = {
17
18
  timeout: 60000,
@@ -253,6 +254,89 @@ export class OpenRouterProvider implements AiProvider {
253
254
  params.max_tokens = input.parameters.maxOutputTokens;
254
255
  }
255
256
 
257
+ // Use structured output API if JSON mode is enabled
258
+ if (input.parameters?.jsonMode) {
259
+ // Define the JSON schema for agent response
260
+ const agentResponseSchema = {
261
+ type: "object",
262
+ properties: {
263
+ message: {
264
+ type: "string",
265
+ description: "The actual message to send to the user",
266
+ },
267
+ route: {
268
+ type: ["string", "null"],
269
+ description:
270
+ "The title of the route chosen (or null if no specific route)",
271
+ },
272
+ state: {
273
+ type: ["string", "null"],
274
+ description:
275
+ "The current state within the route (or null if not in a route)",
276
+ },
277
+ toolCalls: {
278
+ type: "array",
279
+ items: {
280
+ type: "object",
281
+ properties: {
282
+ toolName: {
283
+ type: "string",
284
+ description: "Name of the tool to call",
285
+ },
286
+ arguments: {
287
+ type: "object",
288
+ description: "Arguments to pass to the tool",
289
+ },
290
+ },
291
+ required: ["toolName", "arguments"],
292
+ },
293
+ description: "Tool calls the agent wants to execute",
294
+ },
295
+ reasoning: {
296
+ type: "string",
297
+ description: "Optional: Internal reasoning for this response",
298
+ },
299
+ },
300
+ required: ["message"],
301
+ additionalProperties: false,
302
+ };
303
+
304
+ const response = await this.client.responses.parse({
305
+ model,
306
+ instructions: input.prompt,
307
+ input: "",
308
+ reasoning: {
309
+ effort: input.parameters?.reasoning?.effort || "low",
310
+ },
311
+ text: {
312
+ format: {
313
+ type: "json_schema",
314
+ name: "agentResponseSchema",
315
+ schema: agentResponseSchema,
316
+ },
317
+ },
318
+ });
319
+
320
+ if (!response.output_parsed) {
321
+ throw new Error("No parsed output returned from OpenRouter");
322
+ }
323
+
324
+ const structured = response.output_parsed as AgentStructuredResponse;
325
+ const message = structured.message;
326
+
327
+ return {
328
+ message,
329
+ metadata: {
330
+ model: response.model,
331
+ tokensUsed: response.usage?.total_tokens,
332
+ promptTokens: response.usage?.input_tokens,
333
+ completionTokens: response.usage?.output_tokens,
334
+ },
335
+ structured,
336
+ };
337
+ }
338
+
339
+ // Fall back to regular chat completions API if JSON mode not enabled
256
340
  const response = await this.client.chat.completions.create(params);
257
341
 
258
342
  const message = response.choices[0]?.message?.content;
package/src/types/ai.ts CHANGED
@@ -45,11 +45,34 @@ export interface GenerateMessageInput<TContext = unknown> {
45
45
  maxOutputTokens?: number;
46
46
  /** Reasoning/thinking configuration */
47
47
  reasoning?: ReasoningConfig;
48
+ /** Enable structured JSON output mode */
49
+ jsonMode?: boolean;
48
50
  };
49
51
  /** Abort signal for cancellation */
50
52
  signal?: AbortSignal;
51
53
  }
52
54
 
55
+ /**
56
+ * Structured response from AI containing message and metadata
57
+ */
58
+ export interface AgentStructuredResponse {
59
+ /** The actual message to send to the user */
60
+ message: string;
61
+ /** Route chosen by the agent (route title or null if no route) */
62
+ route?: string | null;
63
+ /** Current state within the route (state description or null) */
64
+ state?: string | null;
65
+ /** Tool calls the agent wants to execute */
66
+ toolCalls?: Array<{
67
+ /** Name of the tool to call */
68
+ toolName: string;
69
+ /** Arguments to pass to the tool */
70
+ arguments: Record<string, unknown>;
71
+ }>;
72
+ /** Additional reasoning or internal thoughts (optional) */
73
+ reasoning?: string;
74
+ }
75
+
53
76
  /**
54
77
  * Output from AI message generation
55
78
  */
@@ -67,6 +90,8 @@ export interface GenerateMessageOutput {
67
90
  /** Additional provider-specific data */
68
91
  [key: string]: unknown;
69
92
  };
93
+ /** Structured response data (when JSON mode is enabled) */
94
+ structured?: AgentStructuredResponse;
70
95
  }
71
96
 
72
97
  /**