genaicode 2.0.0 → 2.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 (44) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +102 -1
  3. package/dist/core/client.d.ts +9 -3
  4. package/dist/core/client.js +69 -0
  5. package/dist/core/client.js.map +1 -1
  6. package/dist/core/errors.d.ts +38 -0
  7. package/dist/core/errors.js +118 -0
  8. package/dist/core/errors.js.map +1 -0
  9. package/dist/core/middleware.d.ts +43 -0
  10. package/dist/core/middleware.js +173 -0
  11. package/dist/core/middleware.js.map +1 -0
  12. package/dist/core/plugins.d.ts +8 -1
  13. package/dist/core/plugins.js +25 -1
  14. package/dist/core/plugins.js.map +1 -1
  15. package/dist/core/result.d.ts +2 -2
  16. package/dist/core/result.js +5 -1
  17. package/dist/core/result.js.map +1 -1
  18. package/dist/core/stream.d.ts +11 -0
  19. package/dist/core/stream.js +93 -0
  20. package/dist/core/stream.js.map +1 -0
  21. package/dist/core/types.d.ts +50 -0
  22. package/dist/index.d.ts +6 -1
  23. package/dist/index.js +3 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/providers/anthropic.js +63 -1
  26. package/dist/providers/anthropic.js.map +1 -1
  27. package/dist/providers/fixtures/multimodal-tool-roundtrip.d.ts +4 -0
  28. package/dist/providers/fixtures/multimodal-tool-roundtrip.js +32 -0
  29. package/dist/providers/fixtures/multimodal-tool-roundtrip.js.map +1 -0
  30. package/dist/providers/google.js +62 -0
  31. package/dist/providers/google.js.map +1 -1
  32. package/dist/providers/openai-converter.d.ts +18 -1
  33. package/dist/providers/openai-converter.js +82 -0
  34. package/dist/providers/openai-converter.js.map +1 -1
  35. package/dist/providers/openai.js +17 -1
  36. package/dist/providers/openai.js.map +1 -1
  37. package/dist/providers.d.ts +1 -1
  38. package/dist/providers.js +1 -1
  39. package/dist/providers.js.map +1 -1
  40. package/docs/pivot.md +30 -14
  41. package/docs/provider-packages.md +60 -0
  42. package/docs/retry.md +62 -0
  43. package/docs/semver.md +55 -0
  44. package/package.json +3 -1
@@ -0,0 +1,173 @@
1
+ import { definePlugin } from './plugins.js';
2
+ import { providerStream } from './stream.js';
3
+ /** Observability middleware: records wall-clock duration around each generate call. */
4
+ export function timingPlugin(options = {}) {
5
+ const onTiming = options.onTiming ??
6
+ ((info) => {
7
+ console.log(`[genaicode] ${info.plugin} ${info.ok ? 'ok' : 'error'} in ${info.durationMs.toFixed(1)}ms`);
8
+ });
9
+ return definePlugin({
10
+ name: 'timing',
11
+ async generate(request, next) {
12
+ const startedAt = performance.now();
13
+ try {
14
+ const result = await next(request);
15
+ onTiming({ plugin: 'timing', durationMs: performance.now() - startedAt, ok: true });
16
+ return result;
17
+ }
18
+ catch (error) {
19
+ onTiming({ plugin: 'timing', durationMs: performance.now() - startedAt, ok: false });
20
+ throw error;
21
+ }
22
+ },
23
+ });
24
+ }
25
+ /** Simple concurrency / interval limiter. Does not talk to external rate-limit stores. */
26
+ export function rateLimitPlugin(options = {}) {
27
+ const concurrency = Math.max(1, options.concurrency ?? 1);
28
+ const minIntervalMs = Math.max(0, options.minIntervalMs ?? 0);
29
+ let active = 0;
30
+ let lastStartedAt = 0;
31
+ const waiters = [];
32
+ const pump = () => {
33
+ while (active < concurrency && waiters.length > 0) {
34
+ const now = Date.now();
35
+ // Enforce spacing between starts even when no call is currently in flight
36
+ // (important for concurrency=1 + minIntervalMs).
37
+ const waitMs = lastStartedAt === 0 ? 0 : Math.max(0, minIntervalMs - (now - lastStartedAt));
38
+ if (waitMs > 0) {
39
+ setTimeout(pump, waitMs);
40
+ return;
41
+ }
42
+ const next = waiters.shift();
43
+ if (!next)
44
+ return;
45
+ active += 1;
46
+ lastStartedAt = Date.now();
47
+ next();
48
+ }
49
+ };
50
+ const acquire = () => new Promise((resolve) => {
51
+ waiters.push(resolve);
52
+ pump();
53
+ });
54
+ const release = () => {
55
+ active = Math.max(0, active - 1);
56
+ pump();
57
+ };
58
+ return definePlugin({
59
+ name: 'rate-limit',
60
+ async generate(request, next) {
61
+ await acquire();
62
+ try {
63
+ return await next(request);
64
+ }
65
+ finally {
66
+ release();
67
+ }
68
+ },
69
+ });
70
+ }
71
+ function defaultCacheKey(request) {
72
+ return JSON.stringify({
73
+ prompt: request.prompt,
74
+ model: request.model,
75
+ temperature: request.temperature,
76
+ maxOutputTokens: request.maxOutputTokens,
77
+ tools: request.tools,
78
+ toolChoice: request.toolChoice,
79
+ metadata: request.metadata,
80
+ });
81
+ }
82
+ /** In-memory response cache. Intentionally short-circuits `next` on hit. */
83
+ export function cachePlugin(options = {}) {
84
+ const keyOf = options.key ?? defaultCacheKey;
85
+ const maxEntries = options.maxEntries ?? 128;
86
+ const cache = new Map();
87
+ return definePlugin({
88
+ name: 'cache',
89
+ async generate(request, next) {
90
+ const key = keyOf(request);
91
+ const hit = cache.get(key);
92
+ if (hit) {
93
+ cache.delete(key);
94
+ cache.set(key, hit);
95
+ return hit;
96
+ }
97
+ const result = await next(request);
98
+ cache.set(key, result);
99
+ if (cache.size > maxEntries) {
100
+ const oldest = cache.keys().next().value;
101
+ if (oldest !== undefined)
102
+ cache.delete(oldest);
103
+ }
104
+ return result;
105
+ },
106
+ });
107
+ }
108
+ /**
109
+ * Fallback middleware: on primary failure, try alternate providers in order.
110
+ * Each alternate receives the same GenerationRequest.
111
+ */
112
+ export function fallbackPlugin(options) {
113
+ const shouldFallback = options.shouldFallback ?? (() => true);
114
+ return definePlugin({
115
+ name: 'fallback',
116
+ async generate(request, next) {
117
+ try {
118
+ return await next(request);
119
+ }
120
+ catch (error) {
121
+ if (!shouldFallback(error, 0))
122
+ throw error;
123
+ let lastError = error;
124
+ for (let index = 0; index < options.providers.length; index += 1) {
125
+ if (!shouldFallback(lastError, index + 1) && index > 0)
126
+ throw lastError;
127
+ try {
128
+ return await options.providers[index].generate(request);
129
+ }
130
+ catch (fallbackError) {
131
+ lastError = fallbackError;
132
+ }
133
+ }
134
+ throw lastError;
135
+ }
136
+ },
137
+ });
138
+ }
139
+ /**
140
+ * Compose multiple providers into one ModelProvider that tries each until one succeeds.
141
+ * Prefer this when there is no single "primary" provider wired through plugins.
142
+ */
143
+ export function fallbackProvider(providers, name = 'fallback') {
144
+ if (providers.length === 0) {
145
+ throw new Error('fallbackProvider requires at least one provider.');
146
+ }
147
+ const streamProvider = providers.find((provider) => Boolean(provider.stream)) ?? providers[0];
148
+ return {
149
+ name,
150
+ capabilities: {
151
+ streaming: providers.some((provider) => provider.capabilities?.streaming || Boolean(provider.stream)),
152
+ tools: providers.every((provider) => provider.capabilities?.tools !== false),
153
+ systemPrompt: providers.every((provider) => provider.capabilities?.systemPrompt !== false),
154
+ },
155
+ async generate(request) {
156
+ let lastError;
157
+ for (const provider of providers) {
158
+ try {
159
+ return await provider.generate(request);
160
+ }
161
+ catch (error) {
162
+ lastError = error;
163
+ }
164
+ }
165
+ throw lastError instanceof Error ? lastError : new Error(String(lastError));
166
+ },
167
+ stream(request) {
168
+ // Prefer the first provider with a native stream implementation.
169
+ return providerStream(streamProvider, request);
170
+ },
171
+ };
172
+ }
173
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/core/middleware.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,14 @@
1
- import type { GenerationRequest, GenerationResult, ModelProvider } from './types.js';
1
+ import type { GenerationRequest, GenerationResult, ModelProvider, StreamEvent } from './types.js';
2
2
  export type GenerateNext = (request?: GenerationRequest) => Promise<GenerationResult>;
3
+ export type StreamNext = (request?: GenerationRequest) => AsyncIterable<StreamEvent>;
3
4
  export interface GenAIPlugin {
4
5
  readonly name: string;
5
6
  generate(request: GenerationRequest, next: GenerateNext): Promise<GenerationResult>;
7
+ /**
8
+ * Optional streaming middleware. When omitted, the stream path passes through
9
+ * to the next plugin / provider unchanged.
10
+ */
11
+ stream?(request: GenerationRequest, next: StreamNext): AsyncIterable<StreamEvent>;
6
12
  }
7
13
  export declare function definePlugin(plugin: GenAIPlugin): GenAIPlugin;
8
14
  /**
@@ -10,5 +16,6 @@ export declare function definePlugin(plugin: GenAIPlugin): GenAIPlugin;
10
16
  *
11
17
  * Plugins run in registration order. Each plugin may modify the request passed
12
18
  * to `next`, transform its result, handle an error, or intentionally short-circuit.
19
+ * Streaming uses the same order; plugins without `stream` pass through.
13
20
  */
14
21
  export declare function withPlugins(provider: ModelProvider, plugins: readonly GenAIPlugin[]): ModelProvider;
@@ -1,3 +1,4 @@
1
+ import { generateAsStream, providerStream } from './stream.js';
1
2
  export function definePlugin(plugin) {
2
3
  return plugin;
3
4
  }
@@ -6,12 +7,14 @@ export function definePlugin(plugin) {
6
7
  *
7
8
  * Plugins run in registration order. Each plugin may modify the request passed
8
9
  * to `next`, transform its result, handle an error, or intentionally short-circuit.
10
+ * Streaming uses the same order; plugins without `stream` pass through.
9
11
  */
10
12
  export function withPlugins(provider, plugins) {
11
13
  if (plugins.length === 0)
12
14
  return provider;
13
- return {
15
+ const wrapped = {
14
16
  name: provider.name,
17
+ capabilities: provider.capabilities,
15
18
  generate(request) {
16
19
  let lastIndex = -1;
17
20
  const dispatch = (index, currentRequest) => {
@@ -26,6 +29,27 @@ export function withPlugins(provider, plugins) {
26
29
  };
27
30
  return dispatch(0, request);
28
31
  },
32
+ stream(request) {
33
+ let lastIndex = -1;
34
+ const dispatch = (index, currentRequest) => {
35
+ if (index <= lastIndex) {
36
+ throw new Error('A GenAIcode plugin called next() more than once.');
37
+ }
38
+ lastIndex = index;
39
+ const plugin = plugins[index];
40
+ if (!plugin) {
41
+ return provider.stream
42
+ ? providerStream(provider, currentRequest)
43
+ : generateAsStream((nextRequest) => provider.generate(nextRequest), currentRequest);
44
+ }
45
+ if (plugin.stream) {
46
+ return plugin.stream(currentRequest, (nextRequest = currentRequest) => dispatch(index + 1, nextRequest));
47
+ }
48
+ return dispatch(index + 1, currentRequest);
49
+ };
50
+ return dispatch(0, request);
51
+ },
29
52
  };
53
+ return wrapped;
30
54
  }
31
55
  //# sourceMappingURL=plugins.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/core/plugins.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,YAAY,CAAC,MAAmB;IAC9C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,QAAuB,EAAE,OAA+B;IAClF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE1C,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ,CAAC,OAAO;YACd,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;YACnB,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,cAAiC,EAA6B,EAAE;gBAC/F,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;oBACvB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC,CAAC;gBACvF,CAAC;gBACD,SAAS,GAAG,KAAK,CAAC;gBAClB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM;oBAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;gBACtD,OAAO,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,WAAW,GAAG,cAAc,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;YAC7G,CAAC,CAAC;YACF,OAAO,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/core/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgB/D,MAAM,UAAU,YAAY,CAAC,MAAmB;IAC9C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,QAAuB,EAAE,OAA+B;IAClF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE1C,MAAM,OAAO,GAAkB;QAC7B,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,QAAQ,CAAC,OAAO;YACd,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;YACnB,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,cAAiC,EAA6B,EAAE;gBAC/F,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;oBACvB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC,CAAC;gBACvF,CAAC;gBACD,SAAS,GAAG,KAAK,CAAC;gBAClB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM;oBAAE,OAAO,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;gBACtD,OAAO,MAAM,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,WAAW,GAAG,cAAc,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;YAC7G,CAAC,CAAC;YACF,OAAO,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,CAAC,OAAO;YACZ,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;YACnB,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,cAAiC,EAA8B,EAAE;gBAChG,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;oBACvB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;gBACtE,CAAC;gBACD,SAAS,GAAG,KAAK,CAAC;gBAClB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,QAAQ,CAAC,MAAM;wBACpB,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC;wBAC1C,CAAC,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC;gBACxF,CAAC;gBACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClB,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,WAAW,GAAG,cAAc,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;gBAC3G,CAAC;gBACD,OAAO,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC;YAC7C,CAAC,CAAC;YACF,OAAO,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -1,5 +1,5 @@
1
- import type { GenerationResult, PromptItem, ToolCall } from './types.js';
1
+ import type { GenerationResult, JsonResultParser, PromptItem, ToolCall } from './types.js';
2
2
  export declare function resultText(result: GenerationResult): string;
3
3
  export declare function resultToolCalls(result: GenerationResult): ToolCall[];
4
4
  export declare function resultToPromptItem(result: GenerationResult): PromptItem;
5
- export declare function parseJsonResult<T = unknown>(result: GenerationResult, parse?: (value: unknown) => T): T;
5
+ export declare function parseJsonResult<T = unknown>(result: GenerationResult, parse?: JsonResultParser<T>): T;
@@ -18,6 +18,10 @@ export function resultToPromptItem(result) {
18
18
  export function parseJsonResult(result, parse = (value) => value) {
19
19
  const text = resultText(result).trim();
20
20
  const fenced = /^```(?:json)?\s*([\s\S]*?)\s*```$/i.exec(text);
21
- return parse(JSON.parse(fenced?.[1] ?? text));
21
+ const parsedValue = JSON.parse(fenced?.[1] ?? text);
22
+ if (typeof parse === 'function') {
23
+ return parse(parsedValue);
24
+ }
25
+ return parse.parse(parsedValue);
22
26
  }
23
27
  //# sourceMappingURL=result.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/core/result.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,MAAwB;IACjD,OAAO,MAAM,CAAC,KAAK;SAChB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;SACtC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;SACxB,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAwB;IACtD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC9F,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAwB;IACzD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,SAAS;QACrC,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC;QAClC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;KACvF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,MAAwB,EACxB,QAA+B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAU;IAEpD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAY,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/core/result.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,UAAU,CAAC,MAAwB;IACjD,OAAO,MAAM,CAAC,KAAK;SAChB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;SACtC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;SACxB,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAwB;IACtD,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AAC9F,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAwB;IACzD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,IAAI,SAAS;QACrC,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC;QAClC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;KACvF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,MAAwB,EACxB,QAA6B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAU;IAElD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAY,CAAC;IAC/D,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { GenerationResult, ModelProvider, StreamEvent } from './types.js';
2
+ /**
3
+ * Turn a completed generation into a short synthetic stream.
4
+ * Used when a provider (or plugin chain) has no native `stream` implementation.
5
+ */
6
+ export declare function generateAsStream(generate: (request: Parameters<ModelProvider['generate']>[0]) => Promise<GenerationResult>, request: Parameters<ModelProvider['generate']>[0]): AsyncGenerator<StreamEvent>;
7
+ export declare function providerStream(provider: ModelProvider, request: Parameters<ModelProvider['generate']>[0]): AsyncIterable<StreamEvent>;
8
+ /** Collect a stream into the final `GenerationResult` (from `done` or by accumulation). */
9
+ export declare function collectStream(events: AsyncIterable<StreamEvent>): Promise<GenerationResult>;
10
+ /** Yield only text deltas from a stream. */
11
+ export declare function streamTextDeltas(events: AsyncIterable<StreamEvent>): AsyncGenerator<string>;
@@ -0,0 +1,93 @@
1
+ import { resultText, resultToolCalls } from './result.js';
2
+ /**
3
+ * Turn a completed generation into a short synthetic stream.
4
+ * Used when a provider (or plugin chain) has no native `stream` implementation.
5
+ */
6
+ export async function* generateAsStream(generate, request) {
7
+ try {
8
+ const result = await generate(request);
9
+ const text = resultText(result);
10
+ if (text) {
11
+ yield { type: 'text-delta', text };
12
+ }
13
+ for (const toolCall of resultToolCalls(result)) {
14
+ yield { type: 'tool-call', toolCall };
15
+ }
16
+ for (const part of result.parts) {
17
+ if (part.type === 'image') {
18
+ yield { type: 'image', image: part.image };
19
+ }
20
+ }
21
+ if (result.usage) {
22
+ yield { type: 'usage', usage: result.usage };
23
+ }
24
+ yield { type: 'done', result };
25
+ }
26
+ catch (error) {
27
+ yield { type: 'error', error };
28
+ throw error;
29
+ }
30
+ }
31
+ export function providerStream(provider, request) {
32
+ if (provider.stream) {
33
+ return provider.stream(request);
34
+ }
35
+ return generateAsStream((nextRequest) => provider.generate(nextRequest), request);
36
+ }
37
+ /** Collect a stream into the final `GenerationResult` (from `done` or by accumulation). */
38
+ export async function collectStream(events) {
39
+ let text = '';
40
+ const toolCalls = [];
41
+ const images = [];
42
+ let usage;
43
+ let model;
44
+ let finishReason;
45
+ let raw;
46
+ let doneResult;
47
+ for await (const event of events) {
48
+ switch (event.type) {
49
+ case 'text-delta':
50
+ text += event.text;
51
+ break;
52
+ case 'tool-call':
53
+ toolCalls.push(event.toolCall);
54
+ break;
55
+ case 'image':
56
+ images.push({ type: 'image', image: event.image });
57
+ break;
58
+ case 'usage':
59
+ usage = event.usage;
60
+ break;
61
+ case 'done':
62
+ doneResult = event.result;
63
+ break;
64
+ case 'error':
65
+ throw event.error instanceof Error ? event.error : new Error(String(event.error));
66
+ case 'tool-call-delta':
67
+ break;
68
+ }
69
+ }
70
+ if (doneResult) {
71
+ return doneResult;
72
+ }
73
+ const parts = [];
74
+ if (text)
75
+ parts.push({ type: 'text', text });
76
+ for (const toolCall of toolCalls) {
77
+ parts.push({ type: 'toolCall', toolCall });
78
+ }
79
+ parts.push(...images);
80
+ return { parts, model, finishReason, usage, raw };
81
+ }
82
+ /** Yield only text deltas from a stream. */
83
+ export async function* streamTextDeltas(events) {
84
+ for await (const event of events) {
85
+ if (event.type === 'text-delta' && event.text) {
86
+ yield event.text;
87
+ }
88
+ else if (event.type === 'error') {
89
+ throw event.error instanceof Error ? event.error : new Error(String(event.error));
90
+ }
91
+ }
92
+ }
93
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/core/stream.ts"],"names":[],"mappings":"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"}
@@ -76,6 +76,10 @@ export interface GenerationResult {
76
76
  usage?: TokenUsage;
77
77
  raw?: unknown;
78
78
  }
79
+ export interface SchemaAdapter<T = unknown> {
80
+ parse(value: unknown): T;
81
+ }
82
+ export type JsonResultParser<T = unknown> = ((value: unknown) => T) | SchemaAdapter<T>;
79
83
  export type ToolChoice = 'auto' | 'none' | 'required' | {
80
84
  name: string;
81
85
  };
@@ -89,9 +93,55 @@ export interface GenerationRequest {
89
93
  signal?: AbortSignal;
90
94
  metadata?: Record<string, unknown>;
91
95
  }
96
+ /**
97
+ * Provider-neutral streaming event IR.
98
+ *
99
+ * Providers may emit deltas as they arrive. Consumers that only need the final
100
+ * answer can ignore intermediate events and wait for `done`.
101
+ */
102
+ export type StreamEvent = {
103
+ type: 'text-delta';
104
+ text: string;
105
+ } | {
106
+ type: 'tool-call-delta';
107
+ id?: string;
108
+ name?: string;
109
+ argumentsDelta?: string;
110
+ } | {
111
+ type: 'tool-call';
112
+ toolCall: ToolCall;
113
+ } | {
114
+ type: 'image';
115
+ image: PromptImage;
116
+ } | {
117
+ type: 'usage';
118
+ usage: TokenUsage;
119
+ } | {
120
+ type: 'error';
121
+ error: unknown;
122
+ } | {
123
+ type: 'done';
124
+ result: GenerationResult;
125
+ };
126
+ export interface ProviderCapabilities {
127
+ /** Native token streaming via `ModelProvider.stream`. */
128
+ streaming?: boolean;
129
+ /** Function/tool calling. */
130
+ tools?: boolean;
131
+ /** Image input, output, both, or none. */
132
+ images?: false | 'input' | 'output' | 'both';
133
+ /** First-class system prompt / instruction support. */
134
+ systemPrompt?: boolean;
135
+ }
92
136
  export interface ModelProvider {
93
137
  readonly name: string;
138
+ readonly capabilities?: ProviderCapabilities;
94
139
  generate(request: GenerationRequest): Promise<GenerationResult>;
140
+ /**
141
+ * Optional native streaming. When omitted, GenAIcode synthesizes a short
142
+ * stream from `generate` (single text snapshot + `done`).
143
+ */
144
+ stream?(request: GenerationRequest): AsyncIterable<StreamEvent>;
95
145
  }
96
146
  export interface GenerationDefaults {
97
147
  model?: string;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,12 @@
1
1
  export { genaicode } from './core/client.js';
2
2
  export type { ConfigureRequest, Conversation, GenAIClient, GenAIClientOptions, RequestBuilder } from './core/client.js';
3
+ export { classifyError, isRetryable, withRetry } from './core/errors.js';
4
+ export type { ClassifiedError, ErrorClass, RetryOptions } from './core/errors.js';
5
+ export { cachePlugin, fallbackPlugin, fallbackProvider, rateLimitPlugin, timingPlugin, } from './core/middleware.js';
6
+ export type { CachePluginOptions, FallbackPluginOptions, RateLimitPluginOptions, TimingPluginOptions, } from './core/middleware.js';
3
7
  export { definePlugin, withPlugins } from './core/plugins.js';
4
- export type { GenAIPlugin, GenerateNext } from './core/plugins.js';
8
+ export type { GenAIPlugin, GenerateNext, StreamNext } from './core/plugins.js';
5
9
  export { asPrompt, assistant, image, prompt, system, toolResults, toPromptItems, user } from './core/prompt.js';
6
10
  export { parseJsonResult, resultText, resultToPromptItem, resultToolCalls } from './core/result.js';
11
+ export { collectStream, generateAsStream, providerStream, streamTextDeltas } from './core/stream.js';
7
12
  export * from './core/types.js';
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  export { genaicode } from './core/client.js';
2
+ export { classifyError, isRetryable, withRetry } from './core/errors.js';
3
+ export { cachePlugin, fallbackPlugin, fallbackProvider, rateLimitPlugin, timingPlugin, } from './core/middleware.js';
2
4
  export { definePlugin, withPlugins } from './core/plugins.js';
3
5
  export { asPrompt, assistant, image, prompt, system, toolResults, toPromptItems, user } from './core/prompt.js';
4
6
  export { parseJsonResult, resultText, resultToPromptItem, resultToolCalls } from './core/result.js';
7
+ export { collectStream, generateAsStream, providerStream, streamTextDeltas } from './core/stream.js';
5
8
  export * from './core/types.js';
6
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE9D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpG,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EACL,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,YAAY,GACb,MAAM,sBAAsB,CAAC;AAO9B,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE9D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChH,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACpG,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACrG,cAAc,iBAAiB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
- import { fromAnthropicMessage, toAnthropicRequest } from './anthropic-converter.js';
2
+ import { fromAnthropicMessage, toAnthropicRequest, } from './anthropic-converter.js';
3
3
  export function anthropic(options = {}) {
4
4
  const client = new Anthropic({
5
5
  apiKey: options.apiKey ?? process.env.ANTHROPIC_API_KEY,
@@ -8,6 +8,12 @@ export function anthropic(options = {}) {
8
8
  });
9
9
  return {
10
10
  name: 'anthropic',
11
+ capabilities: {
12
+ streaming: true,
13
+ tools: true,
14
+ images: 'input',
15
+ systemPrompt: true,
16
+ },
11
17
  async generate(request) {
12
18
  const model = request.model ?? options.model ?? process.env.ANTHROPIC_MODEL;
13
19
  if (!model) {
@@ -23,6 +29,62 @@ export function anthropic(options = {}) {
23
29
  });
24
30
  return fromAnthropicMessage(message);
25
31
  },
32
+ async *stream(request) {
33
+ const model = request.model ?? options.model ?? process.env.ANTHROPIC_MODEL;
34
+ if (!model) {
35
+ throw new Error('No Anthropic model configured. Pass `model` to anthropic() or the request builder.');
36
+ }
37
+ const defaults = {
38
+ model,
39
+ maxOutputTokens: options.maxOutputTokens,
40
+ thinking: options.thinking,
41
+ };
42
+ const params = toAnthropicRequest(request, defaults);
43
+ const stream = client.messages.stream({ ...params }, { signal: request.signal });
44
+ const toolCalls = new Map();
45
+ for await (const event of stream) {
46
+ if (event.type === 'content_block_start' && event.content_block.type === 'tool_use') {
47
+ toolCalls.set(event.index, {
48
+ id: event.content_block.id,
49
+ name: event.content_block.name,
50
+ arguments: '',
51
+ });
52
+ }
53
+ else if (event.type === 'content_block_delta') {
54
+ if (event.delta.type === 'text_delta') {
55
+ yield { type: 'text-delta', text: event.delta.text };
56
+ }
57
+ else if (event.delta.type === 'input_json_delta') {
58
+ const current = toolCalls.get(event.index);
59
+ if (current) {
60
+ current.arguments += event.delta.partial_json;
61
+ yield {
62
+ type: 'tool-call-delta',
63
+ id: current.id,
64
+ name: current.name,
65
+ argumentsDelta: event.delta.partial_json,
66
+ };
67
+ }
68
+ }
69
+ }
70
+ else if (event.type === 'message_delta' && event.usage) {
71
+ // message_delta only reports output tokens; omit total until finalMessage.
72
+ yield {
73
+ type: 'usage',
74
+ usage: {
75
+ outputTokens: event.usage.output_tokens,
76
+ },
77
+ };
78
+ }
79
+ }
80
+ const result = fromAnthropicMessage(await stream.finalMessage());
81
+ for (const part of result.parts) {
82
+ if (part.type === 'toolCall') {
83
+ yield { type: 'tool-call', toolCall: part.toolCall };
84
+ }
85
+ }
86
+ yield { type: 'done', result };
87
+ },
26
88
  };
27
89
  }
28
90
  //# sourceMappingURL=anthropic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAiC,MAAM,0BAA0B,CAAC;AAWnH,MAAM,UAAU,SAAS,CAAC,UAAoC,EAAE;IAC9D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QACvD,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,KAAK,CAAC,QAAQ,CAAC,OAAO;YACpB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;YAC5E,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,oFAAoF,CAAC,CAAC;YACxG,CAAC;YACD,MAAM,QAAQ,GAA6B;gBACzC,KAAK;gBACL,eAAe,EAAE,OAAO,CAAC,eAAe;gBACxC,QAAQ,EAAE,OAAO,CAAC,QAAQ;aAC3B,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE;gBAClF,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;YACH,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACvC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PromptItem, ToolDefinition } from '../../core/types.js';
2
+ /** Shared multimodal + tool-call prompt used by converter contract tests. */
3
+ export declare const multimodalToolRoundTripPrompt: PromptItem[];
4
+ export declare const sampleTools: ToolDefinition[];
@@ -0,0 +1,32 @@
1
+ /** Shared multimodal + tool-call prompt used by converter contract tests. */
2
+ export const multimodalToolRoundTripPrompt = [
3
+ { type: 'systemPrompt', systemPrompt: 'Return captions and call tools when needed.' },
4
+ {
5
+ type: 'assistant',
6
+ text: 'checking',
7
+ toolCalls: [{ id: 'call-1', name: 'lookup', arguments: { id: 7 } }],
8
+ },
9
+ {
10
+ type: 'user',
11
+ toolResults: [{ callId: 'call-1', name: 'lookup', content: '{"ok":true}' }],
12
+ text: 'continue',
13
+ },
14
+ {
15
+ type: 'user',
16
+ text: 'caption',
17
+ images: [{ mediaType: 'image/png', data: 'aGVsbG8=' }],
18
+ },
19
+ ];
20
+ export const sampleTools = [
21
+ {
22
+ name: 'lookup',
23
+ description: 'Look up a record by id',
24
+ parameters: {
25
+ type: 'object',
26
+ properties: { id: { type: 'number' } },
27
+ required: ['id'],
28
+ additionalProperties: false,
29
+ },
30
+ },
31
+ ];
32
+ //# sourceMappingURL=multimodal-tool-roundtrip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multimodal-tool-roundtrip.js","sourceRoot":"","sources":["../../../src/providers/fixtures/multimodal-tool-roundtrip.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,6BAA6B,GAAiB;IACzD,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,6CAA6C,EAAE;IACrF;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;KACpE;IACD;QACE,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;QAC3E,IAAI,EAAE,UAAU;KACjB;IACD;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;KACvD;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAqB;IAC3C;QACE,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,wBAAwB;QACrC,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YACtC,QAAQ,EAAE,CAAC,IAAI,CAAC;YAChB,oBAAoB,EAAE,KAAK;SAC5B;KACF;CACF,CAAC"}