@flink-app/openai-adapter 2.0.0-alpha.100

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.
@@ -0,0 +1,245 @@
1
+ import type { FlinkToolSchema, LLMAdapter, LLMMessage, LLMStreamChunk } from "@flink-app/flink/ai";
2
+ export interface OpenAIAdapterOptions {
3
+ /**
4
+ * OpenAI API key
5
+ */
6
+ apiKey: string;
7
+ /**
8
+ * Model to use (e.g., "gpt-4.1", "gpt-5", "o4-mini")
9
+ */
10
+ model: string;
11
+ /**
12
+ * Optional: Enable structured outputs with JSON schema validation
13
+ * Ensures the model output matches the provided schema with 100% reliability
14
+ *
15
+ * In Responses API, this is configured via text.format instead of response_format
16
+ * @see https://platform.openai.com/docs/guides/structured-outputs
17
+ */
18
+ structuredOutput?: {
19
+ type: "json_schema";
20
+ name: string;
21
+ description?: string;
22
+ schema: Record<string, unknown>;
23
+ strict?: boolean;
24
+ };
25
+ /**
26
+ * Optional: Enable debug logging for development and troubleshooting
27
+ *
28
+ * When enabled, logs:
29
+ * - Full request parameters sent to OpenAI
30
+ * - Tool call decisions made by the LLM
31
+ * - Token usage and performance metrics
32
+ *
33
+ * @default false
34
+ */
35
+ debug?: boolean;
36
+ }
37
+ /**
38
+ * OpenAI adapter for Flink AI framework using the Responses API
39
+ *
40
+ * Built on OpenAI's Responses API (evolution of Chat Completions) which provides:
41
+ * - Step-aware reasoning: Model can return multiple tool calls in a single response
42
+ * - Explicit tool invocation: Tool calls and results are first-class typed items
43
+ * - Better performance: 3% improvement on SWE-bench, 5% on TAUBench
44
+ *
45
+ * ## Architecture
46
+ *
47
+ * IMPORTANT: The Responses API does NOT run your agent loop.
48
+ *
49
+ * What it provides:
50
+ * - Multiple tool calls in one response (but you execute them)
51
+ * - Structured step types (message, function_call, function_call_output)
52
+ * - Streaming event taxonomy
53
+ *
54
+ * What you still handle:
55
+ * - Tool execution (Flink's AgentRunner does this)
56
+ * - Multi-turn loops (call API → execute tools → call API again)
57
+ * - Deciding when to stop
58
+ * - Managing conversation state
59
+ *
60
+ * This is intentional - it gives you full control over the agent loop.
61
+ *
62
+ * ## Streaming-Only Design
63
+ *
64
+ * This adapter uses streaming as the primary method because:
65
+ * - Better user experience (time-to-first-token <500ms)
66
+ * - Prevents HTTP timeouts for long responses
67
+ * - FlinkAgent's lazy generator pattern supports both streaming and non-streaming UX
68
+ *
69
+ * For non-streaming consumption, simply await the final result:
70
+ * ```typescript
71
+ * const response = agent.execute({ message: "Hello" });
72
+ * const result = await response.result; // Consumes stream but returns final result
73
+ * ```
74
+ *
75
+ * ## System Instructions - Two Approaches
76
+ *
77
+ * You can provide system instructions in two ways:
78
+ *
79
+ * 1. **Top-level instructions** (via Flink's instructions parameter):
80
+ * - Automatically prepended as system message to conversation
81
+ * - Simpler approach, recommended for most cases
82
+ * - Following Vercel AI SDK pattern
83
+ *
84
+ * 2. **System role messages** (via messages array):
85
+ * - Add `{ role: "system", content: "..." }` to messages
86
+ * - Gives more control over message structure and placement
87
+ * - Useful when you need system context at specific points in conversation
88
+ *
89
+ * Both approaches can be used together - system messages in input array + top-level instructions.
90
+ *
91
+ * ## Features
92
+ * - ✅ Step-based tool calling with multiple calls per response
93
+ * - ✅ Streaming support with event-based updates
94
+ * - ✅ Structured outputs with JSON schema (100% reliability)
95
+ * - ✅ Support for all OpenAI models (GPT-4.1, GPT-5, o-series)
96
+ * - ✅ Flexible system instructions (top-level or in messages)
97
+ *
98
+ * ## Basic Usage
99
+ * ```typescript
100
+ * import { OpenAIAdapter } from "@flink-app/openai-adapter";
101
+ * import { FlinkApp } from "@flink-app/flink";
102
+ *
103
+ * const app = new FlinkApp({
104
+ * ai: {
105
+ * llms: {
106
+ * default: new OpenAIAdapter({
107
+ * apiKey: process.env.OPENAI_API_KEY!,
108
+ * model: "gpt-5"
109
+ * }),
110
+ * },
111
+ * },
112
+ * });
113
+ * ```
114
+ *
115
+ * ## Structured Outputs
116
+ * ```typescript
117
+ * const adapter = new OpenAIAdapter({
118
+ * apiKey: process.env.OPENAI_API_KEY!,
119
+ * model: "gpt-5",
120
+ * structuredOutput: {
121
+ * type: "json_schema",
122
+ * name: "user_profile",
123
+ * schema: {
124
+ * type: "object",
125
+ * properties: {
126
+ * name: { type: "string" },
127
+ * age: { type: "number" }
128
+ * },
129
+ * required: ["name", "age"],
130
+ * additionalProperties: false
131
+ * },
132
+ * strict: true
133
+ * }
134
+ * });
135
+ * ```
136
+ */
137
+ export declare class OpenAIAdapter implements LLMAdapter<FlinkToolSchema> {
138
+ private client;
139
+ private model;
140
+ private structuredOutput?;
141
+ debug: boolean;
142
+ private log;
143
+ constructor(options: OpenAIAdapterOptions);
144
+ constructor(apiKey: string, model: string);
145
+ stream(params: {
146
+ instructions: string;
147
+ messages: LLMMessage[];
148
+ tools: FlinkToolSchema[];
149
+ maxTokens: number;
150
+ temperature: number;
151
+ providerMetadata?: Record<string, any>;
152
+ }): AsyncGenerator<LLMStreamChunk>;
153
+ /**
154
+ * Convert Flink messages to OpenAI Responses API input format
155
+ *
156
+ * Always sends full conversation history for reliability.
157
+ *
158
+ * The Responses API accepts:
159
+ * 1. System messages: { role: "system", content: "..." }
160
+ * 2. User messages: { role: "user", content: "..." }
161
+ * 3. Assistant messages: { role: "assistant", content: "..." }
162
+ * 4. Function calls: { type: "function_call", ... } ← From previous responses
163
+ * 5. Function call outputs: { type: "function_call_output", ... } ← Tool results
164
+ *
165
+ * Multi-turn pattern:
166
+ * - User: "What's the weather?"
167
+ * - Assistant: "Let me check" + function_call
168
+ * - function_call_output: "22°C"
169
+ * - Assistant: "It's 22°C and sunny"
170
+ * - User: "What about tomorrow?"
171
+ *
172
+ * All message types are preserved for full conversation context.
173
+ */
174
+ /**
175
+ * Build the OpenAI user content from text/image content blocks.
176
+ * Returns a plain string when there are no images (most common case),
177
+ * or an array of input_text / input_image items for multimodal messages.
178
+ * Returns null if there are no user-facing blocks.
179
+ */
180
+ private buildOpenAIUserContent;
181
+ private convertMessagesToResponsesInput;
182
+ /**
183
+ * Convert Flink tools to Responses API format
184
+ *
185
+ * Responses API uses internally-tagged format:
186
+ * { type: "function", name: "...", description: "...", parameters: {...} }
187
+ *
188
+ * vs Chat Completions externally-tagged format:
189
+ * { type: "function", function: { name: "...", description: "...", parameters: {...} } }
190
+ */
191
+ private convertToolsToResponsesFormat;
192
+ /**
193
+ * Sanitize JSON Schema for OpenAI's strict mode
194
+ *
195
+ * OpenAI's strict mode only supports a subset of JSON Schema.
196
+ * This function removes unsupported keywords that would cause the model
197
+ * to silently fail and return empty {} arguments.
198
+ *
199
+ * Unsupported keywords (stripped):
200
+ * - $schema (meta-schema reference)
201
+ * - minLength, maxLength, pattern (string constraints)
202
+ * - minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf (number constraints)
203
+ * - minItems, maxItems, uniqueItems (array constraints)
204
+ * - minProperties, maxProperties (object constraints)
205
+ * - format (string format hints like "email", "uri")
206
+ *
207
+ * Enforced requirements:
208
+ * - additionalProperties: false on all objects (required by strict mode)
209
+ * - required array must include all properties without default values
210
+ *
211
+ * Note: Removed constraints should be documented in the description field
212
+ * where the model can read them as natural language instructions.
213
+ *
214
+ * Reference: https://platform.openai.com/docs/guides/structured-outputs
215
+ */
216
+ private sanitizeSchemaForStrictMode;
217
+ /**
218
+ * Check if the model is a reasoning model that doesn't support temperature
219
+ *
220
+ * Reasoning models use a different internal process and don't allow temperature control:
221
+ * - o1, o1-mini, o1-preview
222
+ * - o3, o3-mini
223
+ * - gpt-5 (uses reasoning approach)
224
+ */
225
+ private isReasoningModel;
226
+ /**
227
+ * Normalize stop reason from Responses API to Flink format
228
+ *
229
+ * LIMITATION: This mapping is lossy. Responses API provides richer status:
230
+ * - "completed" → "end_turn"
231
+ * - "incomplete" → "max_tokens"
232
+ * - "failed" → "max_tokens" (loses error signal)
233
+ * - "cancelled" → "end_turn" (loses cancellation signal)
234
+ *
235
+ * Future improvement: Add LLMResponse.metadata field to preserve:
236
+ * - Raw status from provider
237
+ * - Safety stop reasons
238
+ * - Tool execution errors
239
+ * - Model-specific error codes
240
+ *
241
+ * For now, we map to Flink's limited set and lose granularity.
242
+ */
243
+ private normalizeStopReason;
244
+ }
245
+ //# sourceMappingURL=OpenAIAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenAIAdapter.d.ts","sourceRoot":"","sources":["../src/OpenAIAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAmB,UAAU,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAcpH,MAAM,WAAW,oBAAoB;IACjC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE;QACf,IAAI,EAAE,aAAa,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IAEF;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,qBAAa,aAAc,YAAW,UAAU,CAAC,eAAe,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,gBAAgB,CAAC,CAA2C;IAC7D,KAAK,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,GAAG,CAAmD;gBAElD,OAAO,EAAE,oBAAoB;gBAC7B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAsBlC,MAAM,CAAC,MAAM,EAAE;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,KAAK,EAAE,eAAe,EAAE,CAAC;QACzB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC1C,GAAG,cAAc,CAAC,cAAc,CAAC;IAyKlC;;;;;;;;;;;;;;;;;;;;OAoBG;IAEH;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAqB9B,OAAO,CAAC,+BAA+B;IAqGvC;;;;;;;;OAQG;IACH,OAAO,CAAC,6BAA6B;IAUrC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,OAAO,CAAC,2BAA2B;IA8JnC;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,mBAAmB;CAkB9B"}