@falai/agent 0.1.0

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 (129) hide show
  1. package/README.md +516 -0
  2. package/dist/constants/index.d.ts +5 -0
  3. package/dist/constants/index.d.ts.map +1 -0
  4. package/dist/constants/index.js +5 -0
  5. package/dist/constants/index.js.map +1 -0
  6. package/dist/core/Agent.d.ts +98 -0
  7. package/dist/core/Agent.d.ts.map +1 -0
  8. package/dist/core/Agent.js +248 -0
  9. package/dist/core/Agent.js.map +1 -0
  10. package/dist/core/DomainRegistry.d.ts +26 -0
  11. package/dist/core/DomainRegistry.d.ts.map +1 -0
  12. package/dist/core/DomainRegistry.js +41 -0
  13. package/dist/core/DomainRegistry.js.map +1 -0
  14. package/dist/core/Events.d.ts +19 -0
  15. package/dist/core/Events.d.ts.map +1 -0
  16. package/dist/core/Events.js +79 -0
  17. package/dist/core/Events.js.map +1 -0
  18. package/dist/core/Observation.d.ts +24 -0
  19. package/dist/core/Observation.d.ts.map +1 -0
  20. package/dist/core/Observation.js +35 -0
  21. package/dist/core/Observation.js.map +1 -0
  22. package/dist/core/PromptBuilder.d.ts +121 -0
  23. package/dist/core/PromptBuilder.d.ts.map +1 -0
  24. package/dist/core/PromptBuilder.js +339 -0
  25. package/dist/core/PromptBuilder.js.map +1 -0
  26. package/dist/core/Route.d.ts +46 -0
  27. package/dist/core/Route.d.ts.map +1 -0
  28. package/dist/core/Route.js +113 -0
  29. package/dist/core/Route.js.map +1 -0
  30. package/dist/core/State.d.ts +50 -0
  31. package/dist/core/State.d.ts.map +1 -0
  32. package/dist/core/State.js +110 -0
  33. package/dist/core/State.js.map +1 -0
  34. package/dist/core/Tool.d.ts +31 -0
  35. package/dist/core/Tool.d.ts.map +1 -0
  36. package/dist/core/Tool.js +33 -0
  37. package/dist/core/Tool.js.map +1 -0
  38. package/dist/core/Transition.d.ts +32 -0
  39. package/dist/core/Transition.d.ts.map +1 -0
  40. package/dist/core/Transition.js +59 -0
  41. package/dist/core/Transition.js.map +1 -0
  42. package/dist/index.d.ts +34 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +26 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/providers/GeminiProvider.d.ts +40 -0
  47. package/dist/providers/GeminiProvider.d.ts.map +1 -0
  48. package/dist/providers/GeminiProvider.js +126 -0
  49. package/dist/providers/GeminiProvider.js.map +1 -0
  50. package/dist/providers/OpenAIProvider.d.ts +42 -0
  51. package/dist/providers/OpenAIProvider.d.ts.map +1 -0
  52. package/dist/providers/OpenAIProvider.js +164 -0
  53. package/dist/providers/OpenAIProvider.js.map +1 -0
  54. package/dist/providers/OpenRouterProvider.d.ts +46 -0
  55. package/dist/providers/OpenRouterProvider.d.ts.map +1 -0
  56. package/dist/providers/OpenRouterProvider.js +171 -0
  57. package/dist/providers/OpenRouterProvider.js.map +1 -0
  58. package/dist/types/agent.d.ts +105 -0
  59. package/dist/types/agent.d.ts.map +1 -0
  60. package/dist/types/agent.js +18 -0
  61. package/dist/types/agent.js.map +1 -0
  62. package/dist/types/ai.d.ts +78 -0
  63. package/dist/types/ai.d.ts.map +1 -0
  64. package/dist/types/ai.js +5 -0
  65. package/dist/types/ai.js.map +1 -0
  66. package/dist/types/history.d.ts +112 -0
  67. package/dist/types/history.d.ts.map +1 -0
  68. package/dist/types/history.js +34 -0
  69. package/dist/types/history.js.map +1 -0
  70. package/dist/types/index.d.ts +14 -0
  71. package/dist/types/index.d.ts.map +1 -0
  72. package/dist/types/index.js +7 -0
  73. package/dist/types/index.js.map +1 -0
  74. package/dist/types/observation.d.ts +25 -0
  75. package/dist/types/observation.d.ts.map +1 -0
  76. package/dist/types/observation.js +5 -0
  77. package/dist/types/observation.js.map +1 -0
  78. package/dist/types/prompt.d.ts +46 -0
  79. package/dist/types/prompt.d.ts.map +1 -0
  80. package/dist/types/prompt.js +16 -0
  81. package/dist/types/prompt.js.map +1 -0
  82. package/dist/types/route.d.ts +59 -0
  83. package/dist/types/route.d.ts.map +1 -0
  84. package/dist/types/route.js +5 -0
  85. package/dist/types/route.js.map +1 -0
  86. package/dist/types/tool.d.ts +46 -0
  87. package/dist/types/tool.d.ts.map +1 -0
  88. package/dist/types/tool.js +5 -0
  89. package/dist/types/tool.js.map +1 -0
  90. package/dist/utils/retry.d.ts +13 -0
  91. package/dist/utils/retry.d.ts.map +1 -0
  92. package/dist/utils/retry.js +70 -0
  93. package/dist/utils/retry.js.map +1 -0
  94. package/docs/API_REFERENCE.md +517 -0
  95. package/docs/CONSTRUCTOR_OPTIONS.md +256 -0
  96. package/docs/CONTRIBUTING.md +481 -0
  97. package/docs/GETTING_STARTED.md +328 -0
  98. package/docs/PROVIDERS.md +472 -0
  99. package/docs/PUBLISHING.md +174 -0
  100. package/docs/README.md +68 -0
  101. package/docs/STRUCTURE.md +32 -0
  102. package/examples/declarative-agent.ts +217 -0
  103. package/examples/healthcare-agent.ts +283 -0
  104. package/examples/openai-agent.ts +167 -0
  105. package/examples/travel-agent.ts +342 -0
  106. package/package.json +73 -0
  107. package/src/constants/index.ts +5 -0
  108. package/src/core/Agent.ts +307 -0
  109. package/src/core/DomainRegistry.ts +50 -0
  110. package/src/core/Events.ts +101 -0
  111. package/src/core/Observation.ts +46 -0
  112. package/src/core/PromptBuilder.ts +511 -0
  113. package/src/core/Route.ts +136 -0
  114. package/src/core/State.ts +153 -0
  115. package/src/core/Tool.ts +54 -0
  116. package/src/core/Transition.ts +66 -0
  117. package/src/index.ts +83 -0
  118. package/src/providers/GeminiProvider.ts +220 -0
  119. package/src/providers/OpenAIProvider.ts +272 -0
  120. package/src/providers/OpenRouterProvider.ts +282 -0
  121. package/src/types/agent.ts +112 -0
  122. package/src/types/ai.ts +85 -0
  123. package/src/types/history.ts +125 -0
  124. package/src/types/index.ts +56 -0
  125. package/src/types/observation.ts +27 -0
  126. package/src/types/prompt.ts +49 -0
  127. package/src/types/route.ts +68 -0
  128. package/src/types/tool.ts +53 -0
  129. package/src/utils/retry.ts +96 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * State in the route DSL
3
+ */
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";
10
+
11
+ let stateIdCounter = 0;
12
+
13
+ /**
14
+ * Represents a state within a route
15
+ */
16
+ export class State {
17
+ public readonly id: string;
18
+ private transitions: Transition[] = [];
19
+ private guidelines: Guideline[] = [];
20
+
21
+ constructor(
22
+ public readonly routeId: string,
23
+ public readonly description?: string
24
+ ) {
25
+ this.id = `state_${++stateIdCounter}`;
26
+ }
27
+
28
+ /**
29
+ * Create a transition from this state to another
30
+ *
31
+ * @param spec - Transition specification (chatState, toolState, or direct state)
32
+ * @param condition - Optional condition for this transition
33
+ * @returns Object with target state that supports chaining
34
+ */
35
+ transitionTo(spec: TransitionSpec, condition?: string): TransitionResult {
36
+ // Handle END_ROUTE
37
+ if (
38
+ spec.state &&
39
+ typeof spec.state === "symbol" &&
40
+ spec.state === END_ROUTE
41
+ ) {
42
+ const endTransition = new Transition(
43
+ this.getRef(),
44
+ { state: END_ROUTE },
45
+ condition
46
+ );
47
+ this.transitions.push(endTransition);
48
+
49
+ // Return a terminal state reference
50
+ return {
51
+ target: this.createTerminalRef(),
52
+ };
53
+ }
54
+
55
+ // Handle direct state reference
56
+ if (spec.state && typeof spec.state !== "symbol") {
57
+ const transition = new Transition(this.getRef(), spec, condition);
58
+ this.transitions.push(transition);
59
+
60
+ return {
61
+ target: this.createStateRefWithTransition(spec.state),
62
+ };
63
+ }
64
+
65
+ // Create new target state for chatState or toolState
66
+ const targetState = new State(this.routeId, spec.chatState);
67
+ const transition = new Transition(this.getRef(), spec, condition);
68
+ transition.setTarget(targetState);
69
+
70
+ this.transitions.push(transition);
71
+
72
+ return {
73
+ target: this.createStateRefWithTransition(
74
+ targetState.getRef(),
75
+ targetState
76
+ ),
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Add a guideline specific to this state
82
+ */
83
+ addGuideline(guideline: Guideline): void {
84
+ this.guidelines.push(guideline);
85
+ }
86
+
87
+ /**
88
+ * Get guidelines for this state
89
+ */
90
+ getGuidelines(): Guideline[] {
91
+ return [...this.guidelines];
92
+ }
93
+
94
+ /**
95
+ * Get all transitions from this state
96
+ */
97
+ getTransitions(): Transition[] {
98
+ return [...this.transitions];
99
+ }
100
+
101
+ /**
102
+ * Get state reference
103
+ */
104
+ getRef(): StateRef {
105
+ return {
106
+ id: this.id,
107
+ routeId: this.routeId,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Create a state reference with transitionTo capability for chaining
113
+ */
114
+ private createStateRefWithTransition(
115
+ ref: StateRef,
116
+ state?: State
117
+ ): StateRef & {
118
+ transitionTo: (
119
+ spec: TransitionSpec,
120
+ condition?: string
121
+ ) => TransitionResult;
122
+ } {
123
+ const stateInstance = state || this;
124
+
125
+ return {
126
+ ...ref,
127
+ transitionTo: (spec: TransitionSpec, condition?: string) =>
128
+ stateInstance.transitionTo(spec, condition),
129
+ };
130
+ }
131
+
132
+ /**
133
+ * Create a terminal state reference (for END_ROUTE)
134
+ */
135
+ private createTerminalRef(): StateRef & {
136
+ transitionTo: (
137
+ spec: TransitionSpec,
138
+ condition?: string
139
+ ) => TransitionResult;
140
+ } {
141
+ const terminalRef: StateRef = {
142
+ id: "END",
143
+ routeId: this.routeId,
144
+ };
145
+
146
+ return {
147
+ ...terminalRef,
148
+ transitionTo: () => {
149
+ throw new Error("Cannot transition from END_ROUTE state");
150
+ },
151
+ };
152
+ }
153
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Tool definition and creation utilities
3
+ */
4
+
5
+ import type {
6
+ ToolContext,
7
+ ToolHandler,
8
+ ToolRef,
9
+ ToolResult,
10
+ } from "@/types/tool";
11
+
12
+ let toolIdCounter = 0;
13
+
14
+ /**
15
+ * Define a new tool with type-safe context and arguments
16
+ *
17
+ * @param name - Name of the tool
18
+ * @param handler - Handler function that executes the tool
19
+ * @param options - Optional configuration
20
+ * @returns A reference to the defined tool
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const getTool = defineTool<MyContext, [string, number], string>(
25
+ * 'get_data',
26
+ * async ({ context }, id, count) => {
27
+ * return { data: `Retrieved ${count} items for ${id}` };
28
+ * }
29
+ * );
30
+ * ```
31
+ */
32
+ export function defineTool<TContext, TArgs extends unknown[], TResult>(
33
+ name: string,
34
+ handler: ToolHandler<TContext, TArgs, TResult>,
35
+ options?: {
36
+ description?: string;
37
+ parameters?: unknown;
38
+ }
39
+ ): ToolRef<TContext, TArgs, TResult> {
40
+ const id = `tool_${++toolIdCounter}_${name}`;
41
+
42
+ return {
43
+ id,
44
+ name,
45
+ handler,
46
+ description: options?.description,
47
+ parameters: options?.parameters,
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Re-export types for convenience
53
+ */
54
+ export type { ToolContext, ToolHandler, ToolRef, ToolResult };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Transition between states in the route DSL
3
+ */
4
+
5
+ import type { StateRef, TransitionSpec } from "@/types/route";
6
+ import type { State } from "@/core/State";
7
+
8
+ /**
9
+ * Represents a transition from one state to another
10
+ */
11
+ export class Transition {
12
+ private target?: State;
13
+
14
+ constructor(
15
+ public readonly source: StateRef,
16
+ public readonly spec: TransitionSpec,
17
+ public readonly condition?: string
18
+ ) {}
19
+
20
+ /**
21
+ * Set the target state for this transition
22
+ */
23
+ setTarget(state: State): void {
24
+ this.target = state;
25
+ }
26
+
27
+ /**
28
+ * Get the target state
29
+ */
30
+ getTarget(): State | undefined {
31
+ return this.target;
32
+ }
33
+
34
+ /**
35
+ * Check if this transition has a condition
36
+ */
37
+ hasCondition(): boolean {
38
+ return !!this.condition;
39
+ }
40
+
41
+ /**
42
+ * Get transition description for logging/debugging
43
+ */
44
+ describe(): string {
45
+ const parts: string[] = [];
46
+
47
+ if (this.spec.chatState) {
48
+ parts.push(`chat: "${this.spec.chatState}"`);
49
+ }
50
+ if (this.spec.toolState) {
51
+ parts.push(`tool: ${this.spec.toolState.name}`);
52
+ }
53
+ if (this.spec.state) {
54
+ if (typeof this.spec.state === "symbol") {
55
+ parts.push("state: END_ROUTE");
56
+ } else {
57
+ parts.push(`state: ${this.spec.state.id}`);
58
+ }
59
+ }
60
+
61
+ const transition = parts.join(", ");
62
+ const conditionPart = this.condition ? ` (when: ${this.condition})` : "";
63
+
64
+ return `${this.source.id} -> [${transition}]${conditionPart}`;
65
+ }
66
+ }
package/src/index.ts ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @falai/agent - Standalone AI Agent framework
3
+ *
4
+ * A strongly-typed, modular agent framework with route DSL and AI provider strategy
5
+ */
6
+
7
+ // Core
8
+ export { Agent } from "./core/Agent";
9
+ export { Route } from "./core/Route";
10
+ export { State } from "./core/State";
11
+ export { Transition } from "./core/Transition";
12
+ export { Observation } from "./core/Observation";
13
+ export { defineTool } from "./core/Tool";
14
+ export { DomainRegistry } from "./core/DomainRegistry";
15
+ export { adaptEvent, createMessageEvent, createToolEvent } from "./core/Events";
16
+ export { PromptBuilder } from "./core/PromptBuilder";
17
+ export type { Customer, AgentInfo } from "./core/PromptBuilder";
18
+ export { BuiltInSection } from "./core/PromptBuilder";
19
+
20
+ // Providers
21
+ export { GeminiProvider } from "./providers/GeminiProvider";
22
+ export type { GeminiProviderOptions } from "./providers/GeminiProvider";
23
+ export { OpenAIProvider } from "./providers/OpenAIProvider";
24
+ export type { OpenAIProviderOptions } from "./providers/OpenAIProvider";
25
+ export { OpenRouterProvider } from "./providers/OpenRouterProvider";
26
+ export type { OpenRouterProviderOptions } from "./providers/OpenRouterProvider";
27
+
28
+ // Constants
29
+ export { END_ROUTE } from "./constants";
30
+
31
+ // Types
32
+ export type {
33
+ AgentOptions,
34
+ Term,
35
+ Guideline,
36
+ Capability,
37
+ GuidelineMatch,
38
+ } from "@/types/agent";
39
+ export { CompositionMode } from "@/types/agent";
40
+
41
+ export type {
42
+ Event,
43
+ EmittedEvent,
44
+ MessageEventData,
45
+ ToolEventData,
46
+ StatusEventData,
47
+ Participant,
48
+ } from "@/types/history";
49
+ export { EventKind, EventSource } from "@/types/history";
50
+
51
+ export type {
52
+ RouteRef,
53
+ StateRef,
54
+ RouteOptions,
55
+ TransitionSpec,
56
+ TransitionResult,
57
+ } from "@/types/route";
58
+
59
+ export type {
60
+ ToolContext,
61
+ ToolResult,
62
+ ToolHandler,
63
+ ToolRef,
64
+ } from "@/types/tool";
65
+
66
+ export type {
67
+ AiProvider,
68
+ GenerateMessageInput,
69
+ GenerateMessageOutput,
70
+ ReasoningConfig,
71
+ } from "@/types/ai";
72
+
73
+ export type {
74
+ PromptSection,
75
+ ContextVariable,
76
+ ContextVariableValue,
77
+ } from "@/types/prompt";
78
+ export { SectionStatus } from "@/types/prompt";
79
+
80
+ export type {
81
+ Observation as IObservation,
82
+ ObservationOptions,
83
+ } from "@/types/observation";
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Google Gemini AI provider implementation with retry and backup models
3
+ */
4
+
5
+ import type {
6
+ GoogleGenAI as GoogleGenAIType,
7
+ GenerateContentConfig,
8
+ GenerateContentResponse,
9
+ } from "@google/genai";
10
+ import { GoogleGenAI } from "@google/genai";
11
+
12
+ import type {
13
+ AiProvider,
14
+ GenerateMessageInput,
15
+ GenerateMessageOutput,
16
+ } from "@/types/ai";
17
+ import { withTimeoutAndRetry } from "@/utils/retry";
18
+
19
+ const DEFAULT_RETRY_CONFIG = {
20
+ timeout: 60000,
21
+ retries: 3,
22
+ };
23
+
24
+ /**
25
+ * Configuration options for Gemini provider
26
+ * Uses types from @google/genai package
27
+ */
28
+ export interface GeminiProviderOptions {
29
+ /** Gemini API key */
30
+ apiKey: string;
31
+ /** Model to use (required) - e.g., "models/gemini-2.5-pro" */
32
+ model: string;
33
+ /** Backup models to try if primary fails (default: []) */
34
+ backupModels?: string[];
35
+ /** Default generation config - uses GenerateContentConfig from @google/genai */
36
+ config?: Partial<GenerateContentConfig>;
37
+ /** Retry configuration */
38
+ retryConfig?: {
39
+ timeout?: number;
40
+ retries?: number;
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Type guard for errors with status/code properties
46
+ */
47
+ interface ErrorWithStatus {
48
+ status?: number;
49
+ code?: number;
50
+ message?: string;
51
+ }
52
+
53
+ /**
54
+ * Determines if an error should trigger backup model usage
55
+ */
56
+ const shouldUseBackupModel = (error: unknown): boolean => {
57
+ const err = error as ErrorWithStatus;
58
+
59
+ if (err?.status === 500 || err?.code === 500) {
60
+ return true;
61
+ }
62
+
63
+ const message = err?.message ?? String(error);
64
+ if (
65
+ message.includes("internal error") ||
66
+ message.includes("Internal error") ||
67
+ message.includes("INTERNAL")
68
+ ) {
69
+ return true;
70
+ }
71
+
72
+ if (
73
+ err?.status === 429 ||
74
+ err?.code === 429 ||
75
+ err?.status === 503 ||
76
+ err?.code === 503
77
+ ) {
78
+ return true;
79
+ }
80
+
81
+ if (message.includes("unavailable") || message.includes("not available")) {
82
+ return true;
83
+ }
84
+
85
+ return false;
86
+ };
87
+
88
+ /**
89
+ * Gemini provider implementation with backup models and retry logic
90
+ */
91
+ export class GeminiProvider implements AiProvider {
92
+ public readonly name = "gemini";
93
+ private genAI: GoogleGenAIType;
94
+ private primaryModel: string;
95
+ private backupModels: string[];
96
+ private config?: Partial<GenerateContentConfig>;
97
+ private retryConfig: { timeout: number; retries: number };
98
+
99
+ constructor(options: GeminiProviderOptions) {
100
+ const { apiKey, model, backupModels = [], config, retryConfig } = options;
101
+
102
+ if (!apiKey) {
103
+ throw new Error("Gemini API key is required");
104
+ }
105
+
106
+ if (!model) {
107
+ throw new Error("Model is required. Example: 'models/gemini-2.5-pro'");
108
+ }
109
+
110
+ this.genAI = new GoogleGenAI({ apiKey });
111
+ this.primaryModel = model;
112
+ this.backupModels = backupModels;
113
+ this.config = config;
114
+ this.retryConfig = {
115
+ timeout: retryConfig?.timeout || DEFAULT_RETRY_CONFIG.timeout,
116
+ retries: retryConfig?.retries || DEFAULT_RETRY_CONFIG.retries,
117
+ };
118
+ }
119
+
120
+ async generateMessage<TContext = unknown>(
121
+ input: GenerateMessageInput<TContext>
122
+ ): Promise<GenerateMessageOutput> {
123
+ return this.generateWithBackup(input);
124
+ }
125
+
126
+ private async generateWithBackup<TContext = unknown>(
127
+ input: GenerateMessageInput<TContext>
128
+ ): Promise<GenerateMessageOutput> {
129
+ // Try primary model first
130
+ try {
131
+ return await this.generateWithModel(this.primaryModel, input);
132
+ } catch (primaryError: unknown) {
133
+ const primaryErrMsg = String(primaryError);
134
+ console.warn(
135
+ `[GEMINI] Primary model ${this.primaryModel} failed: ${primaryErrMsg}`
136
+ );
137
+
138
+ if (!shouldUseBackupModel(primaryError)) {
139
+ throw primaryError;
140
+ }
141
+
142
+ console.log(`[GEMINI] Trying backup models`);
143
+
144
+ let lastBackupError: unknown = primaryError;
145
+
146
+ for (let i = 0; i < this.backupModels.length; i++) {
147
+ const backupModel = this.backupModels[i];
148
+ console.log(
149
+ `[GEMINI] Trying backup model ${i + 1}/${
150
+ this.backupModels.length
151
+ }: ${backupModel}`
152
+ );
153
+
154
+ try {
155
+ const result = await this.generateWithModel(backupModel, input);
156
+ console.log(`[GEMINI] Backup model ${backupModel} succeeded`);
157
+ return result;
158
+ } catch (backupError: unknown) {
159
+ const backupErrMsg = String(backupError);
160
+ console.warn(
161
+ `[GEMINI] Backup model ${backupModel} failed: ${backupErrMsg}`
162
+ );
163
+ lastBackupError = backupError;
164
+
165
+ if (
166
+ !shouldUseBackupModel(backupError) &&
167
+ i < this.backupModels.length - 1
168
+ ) {
169
+ console.log(
170
+ `[GEMINI] Backup model error doesn't qualify for further attempts`
171
+ );
172
+ break;
173
+ }
174
+ }
175
+ }
176
+
177
+ const lastBackupErrMsg = String(lastBackupError);
178
+ console.error(
179
+ `[GEMINI] All models failed. Primary: ${primaryErrMsg}, Last backup: ${lastBackupErrMsg}`
180
+ );
181
+ throw lastBackupError;
182
+ }
183
+ }
184
+
185
+ private async generateWithModel<TContext = unknown>(
186
+ model: string,
187
+ input: GenerateMessageInput<TContext>
188
+ ): Promise<GenerateMessageOutput> {
189
+ const operation = async (): Promise<GenerateMessageOutput> => {
190
+ const response: GenerateContentResponse =
191
+ await this.genAI.models.generateContent({
192
+ model,
193
+ contents: input.prompt,
194
+ config: this.config,
195
+ });
196
+
197
+ const message = response.text;
198
+ if (!message) {
199
+ throw new Error("No response from Gemini");
200
+ }
201
+
202
+ return {
203
+ message,
204
+ metadata: {
205
+ model,
206
+ tokensUsed: response.usageMetadata?.totalTokenCount,
207
+ promptTokens: response.usageMetadata?.promptTokenCount,
208
+ completionTokens: response.usageMetadata?.candidatesTokenCount,
209
+ },
210
+ };
211
+ };
212
+
213
+ return withTimeoutAndRetry(
214
+ operation,
215
+ this.retryConfig.timeout,
216
+ this.retryConfig.retries,
217
+ `Gemini ${model}`
218
+ );
219
+ }
220
+ }