@tanstack/ai 0.43.1 → 0.44.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 (69) hide show
  1. package/dist/esm/activities/chat/messages.js +21 -8
  2. package/dist/esm/activities/chat/messages.js.map +1 -1
  3. package/dist/esm/activities/embed/adapter.d.ts +69 -0
  4. package/dist/esm/activities/embed/adapter.js +23 -0
  5. package/dist/esm/activities/embed/adapter.js.map +1 -0
  6. package/dist/esm/activities/embed/index.d.ts +117 -0
  7. package/dist/esm/activities/embed/index.js +166 -0
  8. package/dist/esm/activities/embed/index.js.map +1 -0
  9. package/dist/esm/activities/error-payload.d.ts +8 -0
  10. package/dist/esm/activities/error-payload.js +29 -17
  11. package/dist/esm/activities/error-payload.js.map +1 -1
  12. package/dist/esm/activities/generateAudio/index.d.ts +12 -0
  13. package/dist/esm/activities/generateAudio/index.js +19 -6
  14. package/dist/esm/activities/generateAudio/index.js.map +1 -1
  15. package/dist/esm/activities/generateImage/index.d.ts +12 -0
  16. package/dist/esm/activities/generateImage/index.js +21 -7
  17. package/dist/esm/activities/generateImage/index.js.map +1 -1
  18. package/dist/esm/activities/generateSpeech/index.d.ts +17 -1
  19. package/dist/esm/activities/generateSpeech/index.js +19 -6
  20. package/dist/esm/activities/generateSpeech/index.js.map +1 -1
  21. package/dist/esm/activities/generateTranscription/index.d.ts +17 -1
  22. package/dist/esm/activities/generateTranscription/index.js +19 -6
  23. package/dist/esm/activities/generateTranscription/index.js.map +1 -1
  24. package/dist/esm/activities/generateVideo/index.d.ts +18 -0
  25. package/dist/esm/activities/generateVideo/index.js +54 -15
  26. package/dist/esm/activities/generateVideo/index.js.map +1 -1
  27. package/dist/esm/activities/index.d.ts +8 -2
  28. package/dist/esm/activities/index.js +11 -7
  29. package/dist/esm/activities/middleware/types.d.ts +1 -1
  30. package/dist/esm/activities/rerank/adapter.d.ts +63 -0
  31. package/dist/esm/activities/rerank/adapter.js +23 -0
  32. package/dist/esm/activities/rerank/adapter.js.map +1 -0
  33. package/dist/esm/activities/rerank/index.d.ts +92 -0
  34. package/dist/esm/activities/rerank/index.js +163 -0
  35. package/dist/esm/activities/rerank/index.js.map +1 -0
  36. package/dist/esm/activities/summarize/index.d.ts +17 -1
  37. package/dist/esm/activities/summarize/index.js +19 -5
  38. package/dist/esm/activities/summarize/index.js.map +1 -1
  39. package/dist/esm/index.d.ts +7 -2
  40. package/dist/esm/index.js +5 -1
  41. package/dist/esm/middlewares/otel.js +20 -2
  42. package/dist/esm/middlewares/otel.js.map +1 -1
  43. package/dist/esm/types.d.ts +195 -0
  44. package/dist/esm/utilities/activity-abort.d.ts +53 -0
  45. package/dist/esm/utilities/activity-abort.js +150 -0
  46. package/dist/esm/utilities/activity-abort.js.map +1 -0
  47. package/dist/esm/utilities/embedding-input.d.ts +32 -0
  48. package/dist/esm/utilities/embedding-input.js +61 -0
  49. package/dist/esm/utilities/embedding-input.js.map +1 -0
  50. package/package.json +3 -3
  51. package/src/activities/chat/messages.ts +30 -1
  52. package/src/activities/embed/adapter.ts +112 -0
  53. package/src/activities/embed/index.ts +318 -0
  54. package/src/activities/error-payload.ts +41 -9
  55. package/src/activities/generateAudio/index.ts +47 -5
  56. package/src/activities/generateImage/index.ts +48 -5
  57. package/src/activities/generateSpeech/index.ts +52 -9
  58. package/src/activities/generateTranscription/index.ts +52 -9
  59. package/src/activities/generateVideo/index.ts +131 -33
  60. package/src/activities/index.ts +44 -0
  61. package/src/activities/middleware/types.ts +2 -0
  62. package/src/activities/rerank/adapter.ts +90 -0
  63. package/src/activities/rerank/index.ts +302 -0
  64. package/src/activities/summarize/index.ts +59 -19
  65. package/src/index.ts +19 -0
  66. package/src/middlewares/otel.ts +38 -3
  67. package/src/types.ts +219 -0
  68. package/src/utilities/activity-abort.ts +197 -0
  69. package/src/utilities/embedding-input.ts +83 -0
@@ -1,17 +1,21 @@
1
1
  import { BaseTextAdapter } from "./chat/adapter.js";
2
- import { chat, kind as kind$3 } from "./chat/index.js";
2
+ import { chat, kind as kind$5 } from "./chat/index.js";
3
3
  import { BaseSummarizeAdapter } from "./summarize/adapter.js";
4
4
  import { ChatStreamSummarizeAdapter } from "./summarize/chat-stream-summarize.js";
5
- import { kind as kind$2, summarize } from "./summarize/index.js";
5
+ import { kind as kind$4, summarize } from "./summarize/index.js";
6
+ import { BaseRerankAdapter } from "./rerank/adapter.js";
7
+ import { createRerankOptions, kind as kind$3, rerank } from "./rerank/index.js";
6
8
  import { BaseImageAdapter } from "./generateImage/adapter.js";
7
- import { generateImage, kind as kind$1 } from "./generateImage/index.js";
9
+ import { generateImage, kind as kind$2 } from "./generateImage/index.js";
8
10
  import { BaseAudioAdapter } from "./generateAudio/adapter.js";
9
11
  import { generateAudio, kind } from "./generateAudio/index.js";
10
12
  import { BaseVideoAdapter } from "./generateVideo/adapter.js";
11
- import { generateVideo, getVideoJobStatus, kind as kind$6 } from "./generateVideo/index.js";
13
+ import { generateVideo, getVideoJobStatus, kind as kind$8 } from "./generateVideo/index.js";
12
14
  import { snapToDurationOption } from "./generateVideo/snap.js";
13
15
  import { BaseTTSAdapter } from "./generateSpeech/adapter.js";
14
- import { generateSpeech, kind as kind$5 } from "./generateSpeech/index.js";
16
+ import { generateSpeech, kind as kind$7 } from "./generateSpeech/index.js";
15
17
  import { BaseTranscriptionAdapter } from "./generateTranscription/adapter.js";
16
- import { generateTranscription, kind as kind$4 } from "./generateTranscription/index.js";
17
- export { BaseAudioAdapter, BaseImageAdapter, BaseSummarizeAdapter, BaseTTSAdapter, BaseTextAdapter, BaseTranscriptionAdapter, BaseVideoAdapter, ChatStreamSummarizeAdapter, kind as audioKind, chat, generateAudio, generateImage, generateSpeech, generateTranscription, generateVideo, getVideoJobStatus, kind$1 as imageKind, snapToDurationOption, summarize, kind$2 as summarizeKind, kind$3 as textKind, kind$4 as transcriptionKind, kind$5 as ttsKind, kind$6 as videoKind };
18
+ import { generateTranscription, kind as kind$6 } from "./generateTranscription/index.js";
19
+ import { BaseEmbeddingAdapter } from "./embed/adapter.js";
20
+ import { embed, kind as kind$1 } from "./embed/index.js";
21
+ export { BaseAudioAdapter, BaseEmbeddingAdapter, BaseImageAdapter, BaseRerankAdapter, BaseSummarizeAdapter, BaseTTSAdapter, BaseTextAdapter, BaseTranscriptionAdapter, BaseVideoAdapter, ChatStreamSummarizeAdapter, kind as audioKind, chat, createRerankOptions, embed, kind$1 as embeddingKind, generateAudio, generateImage, generateSpeech, generateTranscription, generateVideo, getVideoJobStatus, kind$2 as imageKind, rerank, kind$3 as rerankKind, snapToDurationOption, summarize, kind$4 as summarizeKind, kind$5 as textKind, kind$6 as transcriptionKind, kind$7 as ttsKind, kind$8 as videoKind };
@@ -11,7 +11,7 @@ import { TokenUsage } from '../../types.js';
11
11
  * `'summarize'` produces text, not media, so it has no artifacts — a
12
12
  * persistence middleware stores its run record and result and nothing else.
13
13
  */
14
- export type GenerationActivity = 'chat' | 'image' | 'video' | 'audio' | 'tts' | 'transcription' | 'summarize';
14
+ export type GenerationActivity = 'chat' | 'image' | 'video' | 'audio' | 'tts' | 'transcription' | 'embedding' | 'rerank' | 'summarize';
15
15
  /**
16
16
  * Stable context passed to every {@link GenerationMiddleware} hook. Created
17
17
  * once per activity call and shared across the hooks of that call.
@@ -0,0 +1,63 @@
1
+ import { RerankAdapterResult, RerankOptions } from '../../types.js';
2
+ /**
3
+ * Configuration for rerank adapter instances
4
+ */
5
+ export interface RerankAdapterConfig {
6
+ apiKey?: string;
7
+ baseUrl?: string;
8
+ timeout?: number;
9
+ headers?: Record<string, string>;
10
+ }
11
+ /**
12
+ * Rerank adapter interface with pre-resolved generics.
13
+ *
14
+ * An adapter is created by a provider function: `provider('model')` → `adapter`
15
+ * All type resolution happens at the provider call site, not in this interface.
16
+ *
17
+ * Generic parameters:
18
+ * - TModel: The specific model name (e.g. 'rerank-v3.5')
19
+ * - TProviderOptions: Provider-specific options (already resolved)
20
+ */
21
+ export interface RerankAdapter<TModel extends string = string, TProviderOptions extends object = Record<string, unknown>> {
22
+ /** Discriminator for adapter kind */
23
+ readonly kind: 'rerank';
24
+ /** Adapter name identifier */
25
+ readonly name: string;
26
+ /** The model this adapter is configured for */
27
+ readonly model: TModel;
28
+ /**
29
+ * @internal Type-only properties for inference. Not assigned at runtime.
30
+ */
31
+ '~types': {
32
+ providerOptions: TProviderOptions;
33
+ };
34
+ /**
35
+ * Rerank the given (pre-serialized) documents against the query, returning
36
+ * scored indices into `options.documents`. The activity layer maps these
37
+ * back to the caller's original documents.
38
+ */
39
+ rerank: (options: RerankOptions<TProviderOptions>) => Promise<RerankAdapterResult>;
40
+ }
41
+ /**
42
+ * A RerankAdapter with any/unknown type parameters.
43
+ * Useful as a constraint in generic functions and interfaces.
44
+ */
45
+ export type AnyRerankAdapter = RerankAdapter<any, any>;
46
+ /**
47
+ * Abstract base class for rerank adapters.
48
+ * Extend this class to implement a rerank adapter for a specific provider.
49
+ *
50
+ * Generic parameters match RerankAdapter - all pre-resolved by the provider function.
51
+ */
52
+ export declare abstract class BaseRerankAdapter<TModel extends string = string, TProviderOptions extends object = Record<string, unknown>> implements RerankAdapter<TModel, TProviderOptions> {
53
+ readonly kind: "rerank";
54
+ abstract readonly name: string;
55
+ readonly model: TModel;
56
+ '~types': {
57
+ providerOptions: TProviderOptions;
58
+ };
59
+ protected config: RerankAdapterConfig;
60
+ constructor(config: RerankAdapterConfig | undefined, model: TModel);
61
+ abstract rerank(options: RerankOptions<TProviderOptions>): Promise<RerankAdapterResult>;
62
+ protected generateId(): string;
63
+ }
@@ -0,0 +1,23 @@
1
+ //#region src/activities/rerank/adapter.ts
2
+ /**
3
+ * Abstract base class for rerank adapters.
4
+ * Extend this class to implement a rerank adapter for a specific provider.
5
+ *
6
+ * Generic parameters match RerankAdapter - all pre-resolved by the provider function.
7
+ */
8
+ var BaseRerankAdapter = class {
9
+ kind = "rerank";
10
+ model;
11
+ config;
12
+ constructor(config = {}, model) {
13
+ this.config = config;
14
+ this.model = model;
15
+ }
16
+ generateId() {
17
+ return `${this.name}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
18
+ }
19
+ };
20
+ //#endregion
21
+ export { BaseRerankAdapter };
22
+
23
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","names":[],"sources":["../../../../src/activities/rerank/adapter.ts"],"sourcesContent":["import type { RerankAdapterResult, RerankOptions } from '../../types'\n\n/**\n * Configuration for rerank adapter instances\n */\nexport interface RerankAdapterConfig {\n apiKey?: string\n baseUrl?: string\n timeout?: number\n headers?: Record<string, string>\n}\n\n/**\n * Rerank adapter interface with pre-resolved generics.\n *\n * An adapter is created by a provider function: `provider('model')` → `adapter`\n * All type resolution happens at the provider call site, not in this interface.\n *\n * Generic parameters:\n * - TModel: The specific model name (e.g. 'rerank-v3.5')\n * - TProviderOptions: Provider-specific options (already resolved)\n */\nexport interface RerankAdapter<\n TModel extends string = string,\n TProviderOptions extends object = Record<string, unknown>,\n> {\n /** Discriminator for adapter kind */\n readonly kind: 'rerank'\n /** Adapter name identifier */\n readonly name: string\n /** The model this adapter is configured for */\n readonly model: TModel\n\n /**\n * @internal Type-only properties for inference. Not assigned at runtime.\n */\n '~types': {\n providerOptions: TProviderOptions\n }\n\n /**\n * Rerank the given (pre-serialized) documents against the query, returning\n * scored indices into `options.documents`. The activity layer maps these\n * back to the caller's original documents.\n */\n rerank: (\n options: RerankOptions<TProviderOptions>,\n ) => Promise<RerankAdapterResult>\n}\n\n/**\n * A RerankAdapter with any/unknown type parameters.\n * Useful as a constraint in generic functions and interfaces.\n */\nexport type AnyRerankAdapter = RerankAdapter<any, any>\n\n/**\n * Abstract base class for rerank adapters.\n * Extend this class to implement a rerank adapter for a specific provider.\n *\n * Generic parameters match RerankAdapter - all pre-resolved by the provider function.\n */\nexport abstract class BaseRerankAdapter<\n TModel extends string = string,\n TProviderOptions extends object = Record<string, unknown>,\n> implements RerankAdapter<TModel, TProviderOptions> {\n readonly kind = 'rerank' as const\n abstract readonly name: string\n readonly model: TModel\n\n // Type-only property - never assigned at runtime\n declare '~types': {\n providerOptions: TProviderOptions\n }\n\n protected config: RerankAdapterConfig\n\n constructor(config: RerankAdapterConfig = {}, model: TModel) {\n this.config = config\n this.model = model\n }\n\n abstract rerank(\n options: RerankOptions<TProviderOptions>,\n ): Promise<RerankAdapterResult>\n\n protected generateId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n }\n}\n"],"mappings":";;;;;;;AA8DA,IAAsB,oBAAtB,MAGqD;CACnD,OAAgB;CAEhB;CAOA;CAEA,YAAY,SAA8B,CAAC,GAAG,OAAe;EAC3D,KAAK,SAAS;EACd,KAAK,QAAQ;CACf;CAMA,aAA+B;EAC7B,OAAO,GAAG,KAAK,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;CAC5E;AACF"}
@@ -0,0 +1,92 @@
1
+ import { DebugOption } from '../../logger/types.js';
2
+ import { GenerationMiddleware } from '../middleware/types.js';
3
+ import { RerankAdapter } from './adapter.js';
4
+ import { RerankResult } from '../../types.js';
5
+ /** The adapter kind this activity handles */
6
+ export declare const kind: "rerank";
7
+ /** Extract provider options from a RerankAdapter via ~types */
8
+ export type RerankProviderOptions<TAdapter> = TAdapter extends {
9
+ '~types': {
10
+ providerOptions: infer P extends object;
11
+ };
12
+ } ? P : object;
13
+ /**
14
+ * Options for the rerank activity. The model is extracted from the adapter's
15
+ * model property.
16
+ *
17
+ * @template TAdapter - The rerank adapter type
18
+ * @template TDocument - The document element type (string or object)
19
+ */
20
+ export interface RerankActivityOptions<TAdapter extends RerankAdapter<string, RerankProviderOptions<TAdapter>>, TDocument extends string | object = string> {
21
+ /** The rerank adapter to use (must be created with a model) */
22
+ adapter: TAdapter & {
23
+ kind: typeof kind;
24
+ };
25
+ /** The query documents are scored against. */
26
+ query: string;
27
+ /**
28
+ * Documents to rerank. Either strings or JSON-serializable objects — object
29
+ * documents are serialized with `JSON.stringify` before being sent to the
30
+ * provider, and the original element (string or object) is returned in the
31
+ * result, preserving its type.
32
+ */
33
+ documents: Array<TDocument>;
34
+ /** Return only the top N results. */
35
+ topN?: number;
36
+ /** Provider-specific options */
37
+ modelOptions?: RerankProviderOptions<TAdapter>;
38
+ /** Forwarded to the provider request for cancellation. */
39
+ abortSignal?: AbortSignal;
40
+ /**
41
+ * Observe-only middleware notified on start, usage, success, abort, and
42
+ * error. Pass `otelMiddleware()` to emit OpenTelemetry spans, or implement
43
+ * the `GenerationMiddleware` contract for a custom backend.
44
+ */
45
+ middleware?: Array<GenerationMiddleware>;
46
+ /**
47
+ * Enable debug logging. Pass `true` to enable all categories, `false` to
48
+ * silence everything including errors, or a `DebugConfig` object for granular
49
+ * control and/or a custom `Logger`.
50
+ */
51
+ debug?: DebugOption;
52
+ }
53
+ /**
54
+ * Rerank activity - reorders documents by relevance to a query.
55
+ *
56
+ * @example Basic reranking
57
+ * ```ts
58
+ * import { rerank } from '@tanstack/ai'
59
+ * import { cohereRerank } from '@tanstack/ai-cohere'
60
+ *
61
+ * const { ranking, rerankedDocuments } = await rerank({
62
+ * adapter: cohereRerank('rerank-v3.5'),
63
+ * query: 'talk about rain',
64
+ * documents: ['sunny day at the beach', 'rainy afternoon in the city'],
65
+ * topN: 2,
66
+ * })
67
+ *
68
+ * console.log(rerankedDocuments[0]) // 'rainy afternoon in the city'
69
+ * ```
70
+ *
71
+ * @example Reranking object documents
72
+ * ```ts
73
+ * const { ranking } = await rerank({
74
+ * adapter: cohereRerank('rerank-v3.5'),
75
+ * query: 'best laptop for travel',
76
+ * documents: [
77
+ * { id: 1, text: 'A heavy gaming desktop' },
78
+ * { id: 2, text: 'A lightweight ultrabook with all-day battery' },
79
+ * ],
80
+ * })
81
+ *
82
+ * // ranking[0].document is the original object, fully typed.
83
+ * console.log(ranking[0].document.id)
84
+ * ```
85
+ */
86
+ export declare function rerank<TAdapter extends RerankAdapter<string, RerankProviderOptions<TAdapter>>, TDocument extends string | object = string>(options: RerankActivityOptions<TAdapter, TDocument>): Promise<RerankResult<TDocument>>;
87
+ /**
88
+ * Create typed options for the rerank() function without executing.
89
+ */
90
+ export declare function createRerankOptions<TAdapter extends RerankAdapter<string, RerankProviderOptions<TAdapter>>, TDocument extends string | object = string>(options: RerankActivityOptions<TAdapter, TDocument>): RerankActivityOptions<TAdapter, TDocument>;
91
+ export type { RerankAdapter, RerankAdapterConfig, AnyRerankAdapter, } from './adapter.js';
92
+ export { BaseRerankAdapter } from './adapter.js';
@@ -0,0 +1,163 @@
1
+ import { isAbortShapedError } from "../error-payload.js";
2
+ import { resolveDebugOption } from "../../logger/resolve.js";
3
+ import { createGenerationContext, runGenerationAbort, runGenerationError, runGenerationFinish, runGenerationStart, runGenerationUsage } from "../middleware/run.js";
4
+ import "./adapter.js";
5
+ import { aiEventClient } from "@tanstack/ai-event-client";
6
+ //#region src/activities/rerank/index.ts
7
+ /**
8
+ * Rerank Activity
9
+ *
10
+ * Reorders a set of documents by semantic relevance to a query.
11
+ * This is a self-contained module with implementation, types, and JSDoc.
12
+ */
13
+ /** The adapter kind this activity handles */
14
+ var kind = "rerank";
15
+ function createId(prefix) {
16
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
17
+ }
18
+ /** Serialize a document for the provider. Strings pass through untouched. */
19
+ function serializeDocument(document) {
20
+ return typeof document === "string" ? document : JSON.stringify(document);
21
+ }
22
+ function isAbortError(error, signal) {
23
+ if (isAbortShapedError(error)) return true;
24
+ return error instanceof Error ? false : signal?.aborted === true;
25
+ }
26
+ /**
27
+ * Rerank activity - reorders documents by relevance to a query.
28
+ *
29
+ * @example Basic reranking
30
+ * ```ts
31
+ * import { rerank } from '@tanstack/ai'
32
+ * import { cohereRerank } from '@tanstack/ai-cohere'
33
+ *
34
+ * const { ranking, rerankedDocuments } = await rerank({
35
+ * adapter: cohereRerank('rerank-v3.5'),
36
+ * query: 'talk about rain',
37
+ * documents: ['sunny day at the beach', 'rainy afternoon in the city'],
38
+ * topN: 2,
39
+ * })
40
+ *
41
+ * console.log(rerankedDocuments[0]) // 'rainy afternoon in the city'
42
+ * ```
43
+ *
44
+ * @example Reranking object documents
45
+ * ```ts
46
+ * const { ranking } = await rerank({
47
+ * adapter: cohereRerank('rerank-v3.5'),
48
+ * query: 'best laptop for travel',
49
+ * documents: [
50
+ * { id: 1, text: 'A heavy gaming desktop' },
51
+ * { id: 2, text: 'A lightweight ultrabook with all-day battery' },
52
+ * ],
53
+ * })
54
+ *
55
+ * // ranking[0].document is the original object, fully typed.
56
+ * console.log(ranking[0].document.id)
57
+ * ```
58
+ */
59
+ async function rerank(options) {
60
+ const { adapter, query, documents, topN, modelOptions, abortSignal, middleware } = options;
61
+ const model = adapter.model;
62
+ const requestId = createId("rerank");
63
+ const startTime = Date.now();
64
+ const logger = resolveDebugOption(options.debug);
65
+ if (documents.length === 0) throw new Error("rerank() requires at least one document");
66
+ const mwCtx = createGenerationContext({
67
+ requestId,
68
+ activity: "rerank",
69
+ provider: adapter.name,
70
+ model,
71
+ modelOptions,
72
+ createId
73
+ });
74
+ await runGenerationStart(middleware, mwCtx);
75
+ aiEventClient.emit("rerank:request:started", {
76
+ requestId,
77
+ provider: adapter.name,
78
+ model,
79
+ documentCount: documents.length,
80
+ timestamp: startTime
81
+ });
82
+ logger.request(`activity=rerank provider=${adapter.name}`, {
83
+ provider: adapter.name,
84
+ model,
85
+ documentCount: documents.length
86
+ });
87
+ const serialized = documents.map(serializeDocument);
88
+ try {
89
+ const result = await adapter.rerank({
90
+ model,
91
+ query,
92
+ documents: serialized,
93
+ topN,
94
+ modelOptions,
95
+ abortSignal,
96
+ logger
97
+ });
98
+ const ranking = result.ranking.map((r) => {
99
+ const document = documents[r.index];
100
+ if (document === void 0) throw new Error(`rerank(): provider ${adapter.name} returned out-of-range index ${r.index}`);
101
+ return {
102
+ index: r.index,
103
+ score: r.score,
104
+ document
105
+ };
106
+ });
107
+ const rerankedDocuments = ranking.map((r) => r.document);
108
+ const duration = Date.now() - startTime;
109
+ aiEventClient.emit("rerank:request:completed", {
110
+ requestId,
111
+ provider: adapter.name,
112
+ model,
113
+ documentCount: documents.length,
114
+ resultCount: ranking.length,
115
+ duration,
116
+ timestamp: Date.now()
117
+ });
118
+ aiEventClient.emit("rerank:usage", {
119
+ requestId,
120
+ model,
121
+ usage: result.usage,
122
+ timestamp: Date.now()
123
+ });
124
+ logger.output(`activity=rerank results=${ranking.length}`, { resultCount: ranking.length });
125
+ await runGenerationUsage(middleware, mwCtx, result.usage);
126
+ await runGenerationFinish(middleware, mwCtx, {
127
+ duration,
128
+ usage: result.usage
129
+ });
130
+ return {
131
+ id: result.id,
132
+ model,
133
+ ranking,
134
+ rerankedDocuments,
135
+ usage: result.usage
136
+ };
137
+ } catch (error) {
138
+ const duration = Date.now() - startTime;
139
+ if (isAbortError(error, abortSignal)) await runGenerationAbort(middleware, mwCtx, {
140
+ reason: error instanceof Error ? error.message : void 0,
141
+ duration
142
+ });
143
+ else await runGenerationError(middleware, mwCtx, {
144
+ error,
145
+ duration
146
+ });
147
+ logger.errors("rerank activity failed", {
148
+ error,
149
+ source: "rerank"
150
+ });
151
+ throw error;
152
+ }
153
+ }
154
+ /**
155
+ * Create typed options for the rerank() function without executing.
156
+ */
157
+ function createRerankOptions(options) {
158
+ return options;
159
+ }
160
+ //#endregion
161
+ export { createRerankOptions, kind, rerank };
162
+
163
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/activities/rerank/index.ts"],"sourcesContent":["/**\n * Rerank Activity\n *\n * Reorders a set of documents by semantic relevance to a query.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '@tanstack/ai-event-client'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport { isAbortShapedError } from '../error-payload'\nimport {\n createGenerationContext,\n runGenerationAbort,\n runGenerationError,\n runGenerationFinish,\n runGenerationStart,\n runGenerationUsage,\n} from '../middleware/run'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\nimport type { GenerationMiddleware } from '../middleware/types'\nimport type { RerankAdapter } from './adapter'\nimport type { RerankResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'rerank' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/** Extract provider options from a RerankAdapter via ~types */\nexport type RerankProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P extends object }\n}\n ? P\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the rerank activity. The model is extracted from the adapter's\n * model property.\n *\n * @template TAdapter - The rerank adapter type\n * @template TDocument - The document element type (string or object)\n */\nexport interface RerankActivityOptions<\n TAdapter extends RerankAdapter<string, RerankProviderOptions<TAdapter>>,\n TDocument extends string | object = string,\n> {\n /** The rerank adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The query documents are scored against. */\n query: string\n /**\n * Documents to rerank. Either strings or JSON-serializable objects — object\n * documents are serialized with `JSON.stringify` before being sent to the\n * provider, and the original element (string or object) is returned in the\n * result, preserving its type.\n */\n documents: Array<TDocument>\n /** Return only the top N results. */\n topN?: number\n /** Provider-specific options */\n modelOptions?: RerankProviderOptions<TAdapter>\n /** Forwarded to the provider request for cancellation. */\n abortSignal?: AbortSignal\n /**\n * Observe-only middleware notified on start, usage, success, abort, and\n * error. Pass `otelMiddleware()` to emit OpenTelemetry spans, or implement\n * the `GenerationMiddleware` contract for a custom backend.\n */\n middleware?: Array<GenerationMiddleware>\n /**\n * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\n}\n\n// ===========================\n// Helper Functions\n// ===========================\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n/** Serialize a document for the provider. Strings pass through untouched. */\nfunction serializeDocument(document: string | object): string {\n return typeof document === 'string' ? document : JSON.stringify(document)\n}\n\nfunction isAbortError(error: unknown, signal?: AbortSignal): boolean {\n // Prefer the error's own identity over the signal state. A genuine\n // cancellation throws an abort-shaped error (DOM `AbortError`, the OpenRouter\n // SDK's `RequestAbortedError`, …). Classifying on `signal.aborted` alone would\n // misroute a real failure — e.g. the out-of-range-index throw below — to the\n // abort hook whenever a shared/long-lived signal happens to already be\n // aborted, hiding it from `onError` observers.\n if (isAbortShapedError(error)) return true\n // Fall back to signal state only for non-Error throws we can't otherwise\n // identify; a real Error with a non-abort name is never an abort.\n return error instanceof Error ? false : signal?.aborted === true\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Rerank activity - reorders documents by relevance to a query.\n *\n * @example Basic reranking\n * ```ts\n * import { rerank } from '@tanstack/ai'\n * import { cohereRerank } from '@tanstack/ai-cohere'\n *\n * const { ranking, rerankedDocuments } = await rerank({\n * adapter: cohereRerank('rerank-v3.5'),\n * query: 'talk about rain',\n * documents: ['sunny day at the beach', 'rainy afternoon in the city'],\n * topN: 2,\n * })\n *\n * console.log(rerankedDocuments[0]) // 'rainy afternoon in the city'\n * ```\n *\n * @example Reranking object documents\n * ```ts\n * const { ranking } = await rerank({\n * adapter: cohereRerank('rerank-v3.5'),\n * query: 'best laptop for travel',\n * documents: [\n * { id: 1, text: 'A heavy gaming desktop' },\n * { id: 2, text: 'A lightweight ultrabook with all-day battery' },\n * ],\n * })\n *\n * // ranking[0].document is the original object, fully typed.\n * console.log(ranking[0].document.id)\n * ```\n */\nexport async function rerank<\n TAdapter extends RerankAdapter<string, RerankProviderOptions<TAdapter>>,\n TDocument extends string | object = string,\n>(\n options: RerankActivityOptions<TAdapter, TDocument>,\n): Promise<RerankResult<TDocument>> {\n const {\n adapter,\n query,\n documents,\n topN,\n modelOptions,\n abortSignal,\n middleware,\n } = options\n const model = adapter.model\n const requestId = createId('rerank')\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\n\n if (documents.length === 0) {\n throw new Error('rerank() requires at least one document')\n }\n\n const mwCtx = createGenerationContext({\n requestId,\n // `rerank` joins the GenerationActivity union; otel maps it to its own\n // gen_ai.operation.name.\n activity: 'rerank',\n provider: adapter.name,\n model,\n modelOptions,\n createId,\n })\n\n await runGenerationStart(middleware, mwCtx)\n\n aiEventClient.emit('rerank:request:started', {\n requestId,\n provider: adapter.name,\n model,\n documentCount: documents.length,\n timestamp: startTime,\n })\n\n logger.request(`activity=rerank provider=${adapter.name}`, {\n provider: adapter.name,\n model,\n documentCount: documents.length,\n })\n\n // Serialize once; reuse for the request only. Original documents are mapped\n // back by index below so the caller's element type is preserved.\n const serialized = documents.map(serializeDocument)\n\n try {\n const result = await adapter.rerank({\n model,\n query,\n documents: serialized,\n topN,\n modelOptions,\n abortSignal,\n logger,\n })\n\n const ranking = result.ranking.map((r) => {\n const document = documents[r.index]\n if (document === undefined) {\n throw new Error(\n `rerank(): provider ${adapter.name} returned out-of-range index ${r.index}`,\n )\n }\n return { index: r.index, score: r.score, document }\n })\n const rerankedDocuments = ranking.map((r) => r.document)\n\n const duration = Date.now() - startTime\n\n aiEventClient.emit('rerank:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n documentCount: documents.length,\n resultCount: ranking.length,\n duration,\n timestamp: Date.now(),\n })\n\n aiEventClient.emit('rerank:usage', {\n requestId,\n model,\n usage: result.usage,\n timestamp: Date.now(),\n })\n\n logger.output(`activity=rerank results=${ranking.length}`, {\n resultCount: ranking.length,\n })\n\n await runGenerationUsage(middleware, mwCtx, result.usage)\n await runGenerationFinish(middleware, mwCtx, {\n duration,\n usage: result.usage,\n })\n\n return {\n id: result.id,\n model,\n ranking,\n rerankedDocuments,\n usage: result.usage,\n }\n } catch (error) {\n const duration = Date.now() - startTime\n if (isAbortError(error, abortSignal)) {\n await runGenerationAbort(middleware, mwCtx, {\n reason: error instanceof Error ? error.message : undefined,\n duration,\n })\n } else {\n await runGenerationError(middleware, mwCtx, { error, duration })\n }\n logger.errors('rerank activity failed', { error, source: 'rerank' })\n throw error\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the rerank() function without executing.\n */\nexport function createRerankOptions<\n TAdapter extends RerankAdapter<string, RerankProviderOptions<TAdapter>>,\n TDocument extends string | object = string,\n>(\n options: RerankActivityOptions<TAdapter, TDocument>,\n): RerankActivityOptions<TAdapter, TDocument> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n RerankAdapter,\n RerankAdapterConfig,\n AnyRerankAdapter,\n} from './adapter'\nexport { BaseRerankAdapter } from './adapter'\n"],"mappings":";;;;;;;;;;;;;AA6BA,IAAa,OAAO;AA+DpB,SAAS,SAAS,QAAwB;CACxC,OAAO,GAAG,OAAO,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;AACzE;;AAGA,SAAS,kBAAkB,UAAmC;CAC5D,OAAO,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,QAAQ;AAC1E;AAEA,SAAS,aAAa,OAAgB,QAA+B;CAOnE,IAAI,mBAAmB,KAAK,GAAG,OAAO;CAGtC,OAAO,iBAAiB,QAAQ,QAAQ,QAAQ,YAAY;AAC9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,eAAsB,OAIpB,SACkC;CAClC,MAAM,EACJ,SACA,OACA,WACA,MACA,cACA,aACA,eACE;CACJ,MAAM,QAAQ,QAAQ;CACtB,MAAM,YAAY,SAAS,QAAQ;CACnC,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,SAAyB,mBAAmB,QAAQ,KAAK;CAE/D,IAAI,UAAU,WAAW,GACvB,MAAM,IAAI,MAAM,yCAAyC;CAG3D,MAAM,QAAQ,wBAAwB;EACpC;EAGA,UAAU;EACV,UAAU,QAAQ;EAClB;EACA;EACA;CACF,CAAC;CAED,MAAM,mBAAmB,YAAY,KAAK;CAE1C,cAAc,KAAK,0BAA0B;EAC3C;EACA,UAAU,QAAQ;EAClB;EACA,eAAe,UAAU;EACzB,WAAW;CACb,CAAC;CAED,OAAO,QAAQ,4BAA4B,QAAQ,QAAQ;EACzD,UAAU,QAAQ;EAClB;EACA,eAAe,UAAU;CAC3B,CAAC;CAID,MAAM,aAAa,UAAU,IAAI,iBAAiB;CAElD,IAAI;EACF,MAAM,SAAS,MAAM,QAAQ,OAAO;GAClC;GACA;GACA,WAAW;GACX;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,UAAU,OAAO,QAAQ,KAAK,MAAM;GACxC,MAAM,WAAW,UAAU,EAAE;GAC7B,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,MACR,sBAAsB,QAAQ,KAAK,+BAA+B,EAAE,OACtE;GAEF,OAAO;IAAE,OAAO,EAAE;IAAO,OAAO,EAAE;IAAO;GAAS;EACpD,CAAC;EACD,MAAM,oBAAoB,QAAQ,KAAK,MAAM,EAAE,QAAQ;EAEvD,MAAM,WAAW,KAAK,IAAI,IAAI;EAE9B,cAAc,KAAK,4BAA4B;GAC7C;GACA,UAAU,QAAQ;GAClB;GACA,eAAe,UAAU;GACzB,aAAa,QAAQ;GACrB;GACA,WAAW,KAAK,IAAI;EACtB,CAAC;EAED,cAAc,KAAK,gBAAgB;GACjC;GACA;GACA,OAAO,OAAO;GACd,WAAW,KAAK,IAAI;EACtB,CAAC;EAED,OAAO,OAAO,2BAA2B,QAAQ,UAAU,EACzD,aAAa,QAAQ,OACvB,CAAC;EAED,MAAM,mBAAmB,YAAY,OAAO,OAAO,KAAK;EACxD,MAAM,oBAAoB,YAAY,OAAO;GAC3C;GACA,OAAO,OAAO;EAChB,CAAC;EAED,OAAO;GACL,IAAI,OAAO;GACX;GACA;GACA;GACA,OAAO,OAAO;EAChB;CACF,SAAS,OAAO;EACd,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,IAAI,aAAa,OAAO,WAAW,GACjC,MAAM,mBAAmB,YAAY,OAAO;GAC1C,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,KAAA;GACjD;EACF,CAAC;OAED,MAAM,mBAAmB,YAAY,OAAO;GAAE;GAAO;EAAS,CAAC;EAEjE,OAAO,OAAO,0BAA0B;GAAE;GAAO,QAAQ;EAAS,CAAC;EACnE,MAAM;CACR;AACF;;;;AASA,SAAgB,oBAId,SAC4C;CAC5C,OAAO;AACT"}
@@ -5,7 +5,11 @@ import { StreamChunk, SummarizationResult } from '../../types.js';
5
5
  /** The adapter kind this activity handles */
6
6
  export declare const kind: "summarize";
7
7
  /** Extract provider options from a SummarizeAdapter via ~types */
8
- export type SummarizeProviderOptions<TAdapter> = TAdapter extends SummarizeAdapter<any, any> ? TAdapter['~types']['providerOptions'] : object;
8
+ export type SummarizeProviderOptions<TAdapter> = TAdapter extends {
9
+ '~types': {
10
+ providerOptions: infer P extends object;
11
+ };
12
+ } ? P : object;
9
13
  /**
10
14
  * Options for the summarize activity.
11
15
  * The model is extracted from the adapter's model property.
@@ -54,6 +58,18 @@ export interface SummarizeActivityOptions<TAdapter extends SummarizeAdapter<stri
54
58
  * mid-summary fires `onAbort`.
55
59
  */
56
60
  middleware?: Array<GenerationMiddleware>;
61
+ /**
62
+ * Maximum duration of this activity invocation in milliseconds.
63
+ * No SDK-wide default — choose a value suitable for the provider and job.
64
+ * Composed with {@link abortSignal}; the first abort wins.
65
+ */
66
+ timeout?: number;
67
+ /**
68
+ * Caller cancellation signal (request disconnects, job/runtime cancellation).
69
+ * Composed with {@link timeout} into an effective signal forwarded to the
70
+ * adapter. Request-specific — not stored on global provider client config.
71
+ */
72
+ abortSignal?: AbortSignal;
57
73
  /**
58
74
  * Whether to stream the summarization result.
59
75
  * When true, returns an AsyncIterable<StreamChunk> for streaming output.
@@ -1,6 +1,7 @@
1
1
  import { resolveDebugOption } from "../../logger/resolve.js";
2
2
  import { streamGenerationResult } from "../stream-generation-result.js";
3
3
  import { applyGenerationResultTransforms, createGenerationContext, runGenerationAbort, runGenerationError, runGenerationFinish, runGenerationStart, runGenerationUsage } from "../middleware/run.js";
4
+ import { abortReasonMessage, createActivityAbortControls, isActivityAbortError, raceWithAbort } from "../../utilities/activity-abort.js";
4
5
  import "./adapter.js";
5
6
  import "./chat-stream-summarize.js";
6
7
  import { aiEventClient } from "@tanstack/ai-event-client";
@@ -75,12 +76,16 @@ function summarize(options) {
75
76
  * Run non-streaming summarization
76
77
  */
77
78
  async function runSummarize(options) {
78
- const { adapter, text, maxLength, style, focus, modelOptions, middleware } = options;
79
+ const { adapter, text, maxLength, style, focus, modelOptions, middleware, timeout, abortSignal: callerAbortSignal } = options;
79
80
  const model = adapter.model;
80
81
  const requestId = createId("summarize");
81
82
  const inputLength = text.length;
82
83
  const startTime = Date.now();
83
84
  const logger = resolveDebugOption(options.debug);
85
+ const abortControls = createActivityAbortControls({
86
+ timeout,
87
+ abortSignal: callerAbortSignal
88
+ });
84
89
  const mwCtx = createGenerationContext({
85
90
  requestId,
86
91
  activity: "summarize",
@@ -111,10 +116,13 @@ async function runSummarize(options) {
111
116
  style,
112
117
  focus,
113
118
  modelOptions,
114
- logger
119
+ logger,
120
+ ...abortControls.signal ? { abortSignal: abortControls.signal } : {}
115
121
  };
116
122
  try {
117
- const result = await applyGenerationResultTransforms(mwCtx, await adapter.summarize(summarizeOptions));
123
+ const rawResult = await raceWithAbort(adapter.summarize(summarizeOptions), abortControls.signal);
124
+ abortControls.clear();
125
+ const result = await applyGenerationResultTransforms(mwCtx, rawResult);
118
126
  const duration = Date.now() - startTime;
119
127
  const outputLength = result.summary.length;
120
128
  aiEventClient.emit("summarize:request:completed", {
@@ -137,9 +145,15 @@ async function runSummarize(options) {
137
145
  });
138
146
  return result;
139
147
  } catch (error) {
140
- await runGenerationError(middleware, mwCtx, {
148
+ abortControls.clear();
149
+ const duration = Date.now() - startTime;
150
+ if (isActivityAbortError(error, abortControls.signal)) await runGenerationAbort(middleware, mwCtx, {
151
+ reason: abortReasonMessage(error, abortControls.signal),
152
+ duration
153
+ });
154
+ else await runGenerationError(middleware, mwCtx, {
141
155
  error,
142
- duration: Date.now() - startTime
156
+ duration
143
157
  });
144
158
  logger.errors("summarize activity failed", {
145
159
  error,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/activities/summarize/index.ts"],"sourcesContent":["/**\n * Summarize Activity\n *\n * Generates summaries from text input.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '@tanstack/ai-event-client'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport {\n applyGenerationResultTransforms,\n createGenerationContext,\n runGenerationAbort,\n runGenerationError,\n runGenerationFinish,\n runGenerationStart,\n runGenerationUsage,\n} from '../middleware/run'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\nimport type { GenerationMiddleware } from '../middleware/types'\nimport type { SummarizeAdapter } from './adapter'\nimport type { StreamChunk, SummarizationResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'summarize' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/** Extract provider options from a SummarizeAdapter via ~types */\nexport type SummarizeProviderOptions<TAdapter> =\n TAdapter extends SummarizeAdapter<any, any>\n ? TAdapter['~types']['providerOptions']\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the summarize activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The summarize adapter type\n * @template TStream - Whether to stream the output\n */\nexport interface SummarizeActivityOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n> {\n /** The summarize adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The text to summarize */\n text: string\n /** Maximum length of the summary (in words or characters, provider-dependent) */\n maxLength?: number\n /** Style of summary to generate */\n style?: 'bullet-points' | 'paragraph' | 'concise'\n /** Topics or aspects to focus on in the summary */\n focus?: Array<string>\n /** Provider-specific options */\n modelOptions?: SummarizeProviderOptions<TAdapter>\n /**\n * Optional run identity. When set on a streaming summarize, it is stamped\n * onto the emitted `RUN_STARTED` so a delivery-durable route keys the run's\n * log by the same id the client rejoins with — making a mid-run reload\n * resumable. Filed under `threadId` when persistence is wired.\n */\n runId?: string\n /**\n * Stable conversation/thread id for correlating this run when persisted — the\n * slot a reloading client hydrates the last summary by. Pass it whenever\n * persistence is on; `withGenerationPersistence` refuses a run without one.\n */\n threadId?: string\n /**\n * Observe-only middleware notified on start, usage, success, and error. Pass\n * `otelMiddleware()` for OpenTelemetry, `withGenerationPersistence()` to\n * record the run (summaries are text, so the run record holds the result and\n * there are no artifacts to store), or implement the `GenerationMiddleware`\n * contract for a custom backend.\n *\n * Streaming and non-streaming behave the same way: one `onStart`, then a\n * terminal `onFinish` / `onError`, with the result transforms applied to the\n * `SummarizationResult` in between. A streaming consumer that disconnects\n * mid-summary fires `onAbort`.\n */\n middleware?: Array<GenerationMiddleware>\n /**\n * Whether to stream the summarization result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming output.\n * When false or not provided, returns a Promise<SummarizationResult>.\n *\n * @default false\n */\n stream?: TStream\n /**\n * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the summarize activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<SummarizationResult>\n */\nexport type SummarizeActivityResult<TStream extends boolean> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<SummarizationResult>\n\n// ===========================\n// Helper Functions\n// ===========================\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Summarize activity - generates summaries from text.\n *\n * Supports both streaming and non-streaming modes.\n *\n * @example Basic summarization\n * ```ts\n * import { summarize } from '@tanstack/ai'\n * import { openaiSummarize } from '@tanstack/ai-openai'\n *\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...'\n * })\n *\n * console.log(result.summary)\n * ```\n *\n * @example Summarization with style\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * style: 'bullet-points',\n * maxLength: 100\n * })\n * ```\n *\n * @example Focused summarization\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long technical document...',\n * focus: ['key findings', 'methodology']\n * })\n * ```\n *\n * @example Streaming summarization\n * ```ts\n * for await (const chunk of summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * stream: true\n * })) {\n * if (chunk.type === 'content') {\n * process.stdout.write(chunk.delta)\n * }\n * }\n * ```\n */\nexport function summarize<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityResult<TStream> {\n const { stream } = options\n\n if (stream) {\n return runStreamingSummarize(\n options as SummarizeActivityOptions<\n SummarizeAdapter<string, object>,\n true\n >,\n ) as SummarizeActivityResult<TStream>\n }\n\n return runSummarize(\n options as SummarizeActivityOptions<\n SummarizeAdapter<string, object>,\n false\n >,\n ) as SummarizeActivityResult<TStream>\n}\n\n/**\n * Run non-streaming summarization\n */\nasync function runSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, false>,\n): Promise<SummarizationResult> {\n const { adapter, text, maxLength, style, focus, modelOptions, middleware } =\n options\n const model = adapter.model\n const requestId = createId('summarize')\n const inputLength = text.length\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\n\n const mwCtx = createGenerationContext({\n requestId,\n activity: 'summarize',\n provider: adapter.name,\n model,\n modelOptions,\n threadId: options.threadId,\n runId: options.runId,\n createId,\n })\n\n await runGenerationStart(middleware, mwCtx)\n\n aiEventClient.emit('summarize:request:started', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n timestamp: startTime,\n })\n\n logger.request(`activity=summarize provider=${adapter.name}`, {\n provider: adapter.name,\n model,\n inputLength,\n })\n\n const summarizeOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n logger,\n }\n\n try {\n const rawResult = await adapter.summarize(summarizeOptions)\n // Transforms run before anything observes the result — the same order every\n // media activity uses — so the run record and the returned value are the\n // same object.\n const result = await applyGenerationResultTransforms(mwCtx, rawResult)\n\n const duration = Date.now() - startTime\n const outputLength = result.summary.length\n\n aiEventClient.emit('summarize:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n outputLength,\n duration,\n timestamp: Date.now(),\n })\n\n logger.output(`activity=summarize length=${outputLength}`, {\n hasSummary: !!result.summary,\n outputLength,\n })\n\n if (result.usage) await runGenerationUsage(middleware, mwCtx, result.usage)\n await runGenerationFinish(middleware, mwCtx, {\n duration,\n usage: result.usage,\n })\n\n return result\n } catch (error) {\n await runGenerationError(middleware, mwCtx, {\n error,\n duration: Date.now() - startTime,\n })\n logger.errors('summarize activity failed', {\n error,\n source: 'summarize',\n })\n throw error\n }\n}\n\n/** Read a `usage` off a transformed result without asserting its shape. */\nfunction usageOf(result: unknown): SummarizationResult['usage'] | undefined {\n if (typeof result !== 'object' || result === null) return undefined\n const usage = (result as { usage?: unknown }).usage\n return typeof usage === 'object' && usage !== null\n ? (usage as SummarizationResult['usage'])\n : undefined\n}\n\n/**\n * Run streaming summarization\n * Uses the adapter's native streaming if available, otherwise falls back\n * to non-streaming and yields the result as a single chunk.\n */\nasync function* runStreamingSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,\n): AsyncIterable<StreamChunk> {\n const {\n adapter,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n runId,\n threadId,\n } = options\n const model = adapter.model\n const logger: InternalLogger = resolveDebugOption(options.debug)\n\n logger.request(`activity=summarize provider=${adapter.name}`, {\n provider: adapter.name,\n model,\n stream: true,\n })\n\n // Thread the caller's run identity through so the emitted `RUN_STARTED`\n // carries it — keeps a delivery-durable route's log keyed by the id the\n // client rejoins with (mid-run reload resumability). Conditional spreads keep\n // the fields off the object entirely under `exactOptionalPropertyTypes`.\n const summarizeOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n logger,\n ...(runId !== undefined ? { runId } : {}),\n ...(threadId !== undefined ? { threadId } : {}),\n }\n\n // Use real streaming if the adapter supports it\n if (adapter.summarizeStream) {\n yield* runNativeSummarizeStream(\n options,\n adapter.summarizeStream(summarizeOptions),\n logger,\n )\n return\n }\n\n try {\n // Fall back to non-streaming — wrap the result with streamGenerationResult,\n // forwarding the run identity so its RUN_STARTED matches too. The generation\n // itself goes through `runSummarize`, so middleware (and its result\n // transforms) run exactly as they do for a non-streaming call. Only `runId`\n // is taken from the resolved wire identity — `threadId` stays the CALLER's,\n // since a minted one would file the run in a slot no client can hydrate.\n yield* streamGenerationResult(\n (resolved) =>\n runSummarize({ ...options, stream: false, runId: resolved.runId }),\n {\n ...(runId !== undefined ? { runId } : {}),\n ...(threadId !== undefined ? { threadId } : {}),\n },\n )\n } catch (error) {\n logger.errors('summarize activity failed', {\n error,\n source: 'summarize',\n })\n throw error\n }\n}\n\n/**\n * Drive an adapter's native `summarizeStream`, wiring the generation middleware\n * around it.\n *\n * The adapter emits a terminal `generation:result` CUSTOM chunk carrying the\n * assembled {@link SummarizationResult}; that is the one point where a result\n * exists, so the transforms run there and the REWRITTEN result is what gets\n * yielded — the client and the persisted run record then hold the same object.\n * An adapter whose stream never emits one still finishes the run, just with no\n * result recorded.\n */\nasync function* runNativeSummarizeStream(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,\n stream: AsyncIterable<StreamChunk>,\n logger: InternalLogger,\n): AsyncIterable<StreamChunk> {\n const { adapter, middleware, modelOptions } = options\n const mwCtx = createGenerationContext({\n requestId: createId('summarize'),\n activity: 'summarize',\n provider: adapter.name,\n model: adapter.model,\n modelOptions,\n threadId: options.threadId,\n runId: options.runId,\n createId,\n })\n\n await runGenerationStart(middleware, mwCtx)\n\n const startTime = Date.now()\n // Tracks whether a terminal hook already fired, so the `finally` can report an\n // abandoned stream without double-firing. Mirrors the streaming video path.\n let settled = false\n try {\n for await (const chunk of stream) {\n if (chunk.type === 'CUSTOM' && chunk.name === 'generation:result') {\n const result = await applyGenerationResultTransforms<unknown>(\n mwCtx,\n chunk.value,\n )\n const usage = usageOf(result)\n // Finish before yielding the terminal chunks: a consumer that stops\n // reading once it has the result must not trip the abandonment path.\n if (usage) await runGenerationUsage(middleware, mwCtx, usage)\n await runGenerationFinish(middleware, mwCtx, {\n duration: Date.now() - startTime,\n usage,\n })\n settled = true\n yield { ...chunk, value: result }\n continue\n }\n yield chunk\n }\n if (!settled) {\n await runGenerationFinish(middleware, mwCtx, {\n duration: Date.now() - startTime,\n })\n settled = true\n }\n } catch (error) {\n settled = true\n await runGenerationError(middleware, mwCtx, {\n error,\n duration: Date.now() - startTime,\n })\n logger.errors('summarize activity failed', {\n error,\n source: 'summarize',\n })\n throw error\n } finally {\n if (!settled) {\n // The consumer abandoned the stream mid-summary, so the generator is being\n // unwound at a `yield`. Report a cancel, not an error, so an observer ends\n // its span (and persistence marks the run interrupted) instead of leaving\n // the run open forever.\n await runGenerationAbort(middleware, mwCtx, {\n reason: 'Summarize stream abandoned before completion',\n duration: Date.now() - startTime,\n })\n }\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the summarize() function without executing.\n */\nexport function createSummarizeOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n SummarizeAdapter,\n SummarizeAdapterConfig,\n AnySummarizeAdapter,\n} from './adapter'\nexport { BaseSummarizeAdapter } from './adapter'\nexport {\n ChatStreamSummarizeAdapter,\n type ChatStreamCapable,\n type InferTextProviderOptions,\n} from './chat-stream-summarize'\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/activities/summarize/index.ts"],"sourcesContent":["/**\n * Summarize Activity\n *\n * Generates summaries from text input.\n * This is a self-contained module with implementation, types, and JSDoc.\n */\n\nimport { aiEventClient } from '@tanstack/ai-event-client'\nimport { streamGenerationResult } from '../stream-generation-result.js'\nimport { resolveDebugOption } from '../../logger/resolve'\nimport {\n applyGenerationResultTransforms,\n createGenerationContext,\n runGenerationAbort,\n runGenerationError,\n runGenerationFinish,\n runGenerationStart,\n runGenerationUsage,\n} from '../middleware/run'\nimport {\n abortReasonMessage,\n createActivityAbortControls,\n isActivityAbortError,\n raceWithAbort,\n} from '../../utilities/activity-abort'\nimport type { InternalLogger } from '../../logger/internal-logger'\nimport type { DebugOption } from '../../logger/types'\nimport type { GenerationMiddleware } from '../middleware/types'\nimport type { SummarizeAdapter } from './adapter'\nimport type { StreamChunk, SummarizationResult } from '../../types'\n\n// ===========================\n// Activity Kind\n// ===========================\n\n/** The adapter kind this activity handles */\nexport const kind = 'summarize' as const\n\n// ===========================\n// Type Extraction Helpers\n// ===========================\n\n/** Extract provider options from a SummarizeAdapter via ~types */\nexport type SummarizeProviderOptions<TAdapter> = TAdapter extends {\n '~types': { providerOptions: infer P extends object }\n}\n ? P\n : object\n\n// ===========================\n// Activity Options Type\n// ===========================\n\n/**\n * Options for the summarize activity.\n * The model is extracted from the adapter's model property.\n *\n * @template TAdapter - The summarize adapter type\n * @template TStream - Whether to stream the output\n */\nexport interface SummarizeActivityOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n> {\n /** The summarize adapter to use (must be created with a model) */\n adapter: TAdapter & { kind: typeof kind }\n /** The text to summarize */\n text: string\n /** Maximum length of the summary (in words or characters, provider-dependent) */\n maxLength?: number\n /** Style of summary to generate */\n style?: 'bullet-points' | 'paragraph' | 'concise'\n /** Topics or aspects to focus on in the summary */\n focus?: Array<string>\n /** Provider-specific options */\n modelOptions?: SummarizeProviderOptions<TAdapter>\n /**\n * Optional run identity. When set on a streaming summarize, it is stamped\n * onto the emitted `RUN_STARTED` so a delivery-durable route keys the run's\n * log by the same id the client rejoins with — making a mid-run reload\n * resumable. Filed under `threadId` when persistence is wired.\n */\n runId?: string\n /**\n * Stable conversation/thread id for correlating this run when persisted — the\n * slot a reloading client hydrates the last summary by. Pass it whenever\n * persistence is on; `withGenerationPersistence` refuses a run without one.\n */\n threadId?: string\n /**\n * Observe-only middleware notified on start, usage, success, and error. Pass\n * `otelMiddleware()` for OpenTelemetry, `withGenerationPersistence()` to\n * record the run (summaries are text, so the run record holds the result and\n * there are no artifacts to store), or implement the `GenerationMiddleware`\n * contract for a custom backend.\n *\n * Streaming and non-streaming behave the same way: one `onStart`, then a\n * terminal `onFinish` / `onError`, with the result transforms applied to the\n * `SummarizationResult` in between. A streaming consumer that disconnects\n * mid-summary fires `onAbort`.\n */\n middleware?: Array<GenerationMiddleware>\n /**\n * Maximum duration of this activity invocation in milliseconds.\n * No SDK-wide default — choose a value suitable for the provider and job.\n * Composed with {@link abortSignal}; the first abort wins.\n */\n timeout?: number\n /**\n * Caller cancellation signal (request disconnects, job/runtime cancellation).\n * Composed with {@link timeout} into an effective signal forwarded to the\n * adapter. Request-specific — not stored on global provider client config.\n */\n abortSignal?: AbortSignal\n /**\n * Whether to stream the summarization result.\n * When true, returns an AsyncIterable<StreamChunk> for streaming output.\n * When false or not provided, returns a Promise<SummarizationResult>.\n *\n * @default false\n */\n stream?: TStream\n /**\n * Enable debug logging. Pass `true` to enable all categories, `false` to\n * silence everything including errors, or a `DebugConfig` object for granular\n * control and/or a custom `Logger`.\n */\n debug?: DebugOption\n}\n\n// ===========================\n// Activity Result Type\n// ===========================\n\n/**\n * Result type for the summarize activity.\n * - If stream is true: AsyncIterable<StreamChunk>\n * - Otherwise: Promise<SummarizationResult>\n */\nexport type SummarizeActivityResult<TStream extends boolean> =\n TStream extends true\n ? AsyncIterable<StreamChunk>\n : Promise<SummarizationResult>\n\n// ===========================\n// Helper Functions\n// ===========================\n\nfunction createId(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n// ===========================\n// Activity Implementation\n// ===========================\n\n/**\n * Summarize activity - generates summaries from text.\n *\n * Supports both streaming and non-streaming modes.\n *\n * @example Basic summarization\n * ```ts\n * import { summarize } from '@tanstack/ai'\n * import { openaiSummarize } from '@tanstack/ai-openai'\n *\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...'\n * })\n *\n * console.log(result.summary)\n * ```\n *\n * @example Summarization with style\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * style: 'bullet-points',\n * maxLength: 100\n * })\n * ```\n *\n * @example Focused summarization\n * ```ts\n * const result = await summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long technical document...',\n * focus: ['key findings', 'methodology']\n * })\n * ```\n *\n * @example Streaming summarization\n * ```ts\n * for await (const chunk of summarize({\n * adapter: openaiSummarize('gpt-4o-mini'),\n * text: 'Long article text here...',\n * stream: true\n * })) {\n * if (chunk.type === 'content') {\n * process.stdout.write(chunk.delta)\n * }\n * }\n * ```\n */\nexport function summarize<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityResult<TStream> {\n const { stream } = options\n\n if (stream) {\n return runStreamingSummarize(\n options as SummarizeActivityOptions<TAdapter, true>,\n ) as SummarizeActivityResult<TStream>\n }\n\n return runSummarize(\n options as SummarizeActivityOptions<TAdapter, false>,\n ) as SummarizeActivityResult<TStream>\n}\n\n/**\n * Run non-streaming summarization\n */\nasync function runSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, false>,\n): Promise<SummarizationResult> {\n const {\n adapter,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n middleware,\n timeout,\n abortSignal: callerAbortSignal,\n } = options\n const model = adapter.model\n const requestId = createId('summarize')\n const inputLength = text.length\n const startTime = Date.now()\n const logger: InternalLogger = resolveDebugOption(options.debug)\n const abortControls = createActivityAbortControls({\n timeout,\n abortSignal: callerAbortSignal,\n })\n\n const mwCtx = createGenerationContext({\n requestId,\n activity: 'summarize',\n provider: adapter.name,\n model,\n modelOptions,\n threadId: options.threadId,\n runId: options.runId,\n createId,\n })\n\n await runGenerationStart(middleware, mwCtx)\n\n aiEventClient.emit('summarize:request:started', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n timestamp: startTime,\n })\n\n logger.request(`activity=summarize provider=${adapter.name}`, {\n provider: adapter.name,\n model,\n inputLength,\n })\n\n const summarizeOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n logger,\n ...(abortControls.signal ? { abortSignal: abortControls.signal } : {}),\n }\n\n try {\n const rawResult = await raceWithAbort(\n adapter.summarize(summarizeOptions),\n abortControls.signal,\n )\n abortControls.clear()\n // Transforms run before anything observes the result — the same order every\n // media activity uses — so the run record and the returned value are the\n // same object.\n const result = await applyGenerationResultTransforms(mwCtx, rawResult)\n\n const duration = Date.now() - startTime\n const outputLength = result.summary.length\n\n aiEventClient.emit('summarize:request:completed', {\n requestId,\n provider: adapter.name,\n model,\n inputLength,\n outputLength,\n duration,\n timestamp: Date.now(),\n })\n\n logger.output(`activity=summarize length=${outputLength}`, {\n hasSummary: !!result.summary,\n outputLength,\n })\n\n if (result.usage) await runGenerationUsage(middleware, mwCtx, result.usage)\n await runGenerationFinish(middleware, mwCtx, {\n duration,\n usage: result.usage,\n })\n\n return result\n } catch (error) {\n abortControls.clear()\n const duration = Date.now() - startTime\n if (isActivityAbortError(error, abortControls.signal)) {\n await runGenerationAbort(middleware, mwCtx, {\n reason: abortReasonMessage(error, abortControls.signal),\n duration,\n })\n } else {\n await runGenerationError(middleware, mwCtx, {\n error,\n duration,\n })\n }\n logger.errors('summarize activity failed', {\n error,\n source: 'summarize',\n })\n throw error\n }\n}\n\n/** Read a `usage` off a transformed result without asserting its shape. */\nfunction usageOf(result: unknown): SummarizationResult['usage'] | undefined {\n if (typeof result !== 'object' || result === null) return undefined\n const usage = (result as { usage?: unknown }).usage\n return typeof usage === 'object' && usage !== null\n ? (usage as SummarizationResult['usage'])\n : undefined\n}\n\n/**\n * Run streaming summarization\n * Uses the adapter's native streaming if available, otherwise falls back\n * to non-streaming and yields the result as a single chunk.\n */\nasync function* runStreamingSummarize(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,\n): AsyncIterable<StreamChunk> {\n const {\n adapter,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n runId,\n threadId,\n } = options\n const model = adapter.model\n const logger: InternalLogger = resolveDebugOption(options.debug)\n\n logger.request(`activity=summarize provider=${adapter.name}`, {\n provider: adapter.name,\n model,\n stream: true,\n })\n\n // Thread the caller's run identity through so the emitted `RUN_STARTED`\n // carries it — keeps a delivery-durable route's log keyed by the id the\n // client rejoins with (mid-run reload resumability). Conditional spreads keep\n // the fields off the object entirely under `exactOptionalPropertyTypes`.\n const summarizeOptions = {\n model,\n text,\n maxLength,\n style,\n focus,\n modelOptions,\n logger,\n ...(runId !== undefined ? { runId } : {}),\n ...(threadId !== undefined ? { threadId } : {}),\n }\n\n // Use real streaming if the adapter supports it\n if (adapter.summarizeStream) {\n yield* runNativeSummarizeStream(\n options,\n adapter.summarizeStream(summarizeOptions),\n logger,\n )\n return\n }\n\n try {\n // Fall back to non-streaming — wrap the result with streamGenerationResult,\n // forwarding the run identity so its RUN_STARTED matches too. The generation\n // itself goes through `runSummarize`, so middleware (and its result\n // transforms) run exactly as they do for a non-streaming call. Only `runId`\n // is taken from the resolved wire identity — `threadId` stays the CALLER's,\n // since a minted one would file the run in a slot no client can hydrate.\n yield* streamGenerationResult(\n (resolved) =>\n runSummarize({ ...options, stream: false, runId: resolved.runId }),\n {\n ...(runId !== undefined ? { runId } : {}),\n ...(threadId !== undefined ? { threadId } : {}),\n },\n )\n } catch (error) {\n logger.errors('summarize activity failed', {\n error,\n source: 'summarize',\n })\n throw error\n }\n}\n\n/**\n * Drive an adapter's native `summarizeStream`, wiring the generation middleware\n * around it.\n *\n * The adapter emits a terminal `generation:result` CUSTOM chunk carrying the\n * assembled {@link SummarizationResult}; that is the one point where a result\n * exists, so the transforms run there and the REWRITTEN result is what gets\n * yielded — the client and the persisted run record then hold the same object.\n * An adapter whose stream never emits one still finishes the run, just with no\n * result recorded.\n */\nasync function* runNativeSummarizeStream(\n options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>,\n stream: AsyncIterable<StreamChunk>,\n logger: InternalLogger,\n): AsyncIterable<StreamChunk> {\n const { adapter, middleware, modelOptions } = options\n const mwCtx = createGenerationContext({\n requestId: createId('summarize'),\n activity: 'summarize',\n provider: adapter.name,\n model: adapter.model,\n modelOptions,\n threadId: options.threadId,\n runId: options.runId,\n createId,\n })\n\n await runGenerationStart(middleware, mwCtx)\n\n const startTime = Date.now()\n // Tracks whether a terminal hook already fired, so the `finally` can report an\n // abandoned stream without double-firing. Mirrors the streaming video path.\n let settled = false\n try {\n for await (const chunk of stream) {\n if (chunk.type === 'CUSTOM' && chunk.name === 'generation:result') {\n const result = await applyGenerationResultTransforms<unknown>(\n mwCtx,\n chunk.value,\n )\n const usage = usageOf(result)\n // Finish before yielding the terminal chunks: a consumer that stops\n // reading once it has the result must not trip the abandonment path.\n if (usage) await runGenerationUsage(middleware, mwCtx, usage)\n await runGenerationFinish(middleware, mwCtx, {\n duration: Date.now() - startTime,\n usage,\n })\n settled = true\n yield { ...chunk, value: result }\n continue\n }\n yield chunk\n }\n if (!settled) {\n await runGenerationFinish(middleware, mwCtx, {\n duration: Date.now() - startTime,\n })\n settled = true\n }\n } catch (error) {\n settled = true\n await runGenerationError(middleware, mwCtx, {\n error,\n duration: Date.now() - startTime,\n })\n logger.errors('summarize activity failed', {\n error,\n source: 'summarize',\n })\n throw error\n } finally {\n if (!settled) {\n // The consumer abandoned the stream mid-summary, so the generator is being\n // unwound at a `yield`. Report a cancel, not an error, so an observer ends\n // its span (and persistence marks the run interrupted) instead of leaving\n // the run open forever.\n await runGenerationAbort(middleware, mwCtx, {\n reason: 'Summarize stream abandoned before completion',\n duration: Date.now() - startTime,\n })\n }\n }\n}\n\n// ===========================\n// Options Factory\n// ===========================\n\n/**\n * Create typed options for the summarize() function without executing.\n */\nexport function createSummarizeOptions<\n TAdapter extends SummarizeAdapter<string, object>,\n TStream extends boolean = false,\n>(\n options: SummarizeActivityOptions<TAdapter, TStream>,\n): SummarizeActivityOptions<TAdapter, TStream> {\n return options\n}\n\n// Re-export adapter types\nexport type {\n SummarizeAdapter,\n SummarizeAdapterConfig,\n AnySummarizeAdapter,\n} from './adapter'\nexport { BaseSummarizeAdapter } from './adapter'\nexport {\n ChatStreamSummarizeAdapter,\n type ChatStreamCapable,\n type InferTextProviderOptions,\n} from './chat-stream-summarize'\n"],"mappings":";;;;;;;;;;;;;;;AAoCA,IAAa,OAAO;AAgHpB,SAAS,SAAS,QAAwB;CACxC,OAAO,GAAG,OAAO,GAAG,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;AACzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,SAAgB,UAId,SACkC;CAClC,MAAM,EAAE,WAAW;CAEnB,IAAI,QACF,OAAO,sBACL,OACF;CAGF,OAAO,aACL,OACF;AACF;;;;AAKA,eAAe,aACb,SAC8B;CAC9B,MAAM,EACJ,SACA,MACA,WACA,OACA,OACA,cACA,YACA,SACA,aAAa,sBACX;CACJ,MAAM,QAAQ,QAAQ;CACtB,MAAM,YAAY,SAAS,WAAW;CACtC,MAAM,cAAc,KAAK;CACzB,MAAM,YAAY,KAAK,IAAI;CAC3B,MAAM,SAAyB,mBAAmB,QAAQ,KAAK;CAC/D,MAAM,gBAAgB,4BAA4B;EAChD;EACA,aAAa;CACf,CAAC;CAED,MAAM,QAAQ,wBAAwB;EACpC;EACA,UAAU;EACV,UAAU,QAAQ;EAClB;EACA;EACA,UAAU,QAAQ;EAClB,OAAO,QAAQ;EACf;CACF,CAAC;CAED,MAAM,mBAAmB,YAAY,KAAK;CAE1C,cAAc,KAAK,6BAA6B;EAC9C;EACA,UAAU,QAAQ;EAClB;EACA;EACA,WAAW;CACb,CAAC;CAED,OAAO,QAAQ,+BAA+B,QAAQ,QAAQ;EAC5D,UAAU,QAAQ;EAClB;EACA;CACF,CAAC;CAED,MAAM,mBAAmB;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAI,cAAc,SAAS,EAAE,aAAa,cAAc,OAAO,IAAI,CAAC;CACtE;CAEA,IAAI;EACF,MAAM,YAAY,MAAM,cACtB,QAAQ,UAAU,gBAAgB,GAClC,cAAc,MAChB;EACA,cAAc,MAAM;EAIpB,MAAM,SAAS,MAAM,gCAAgC,OAAO,SAAS;EAErE,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,MAAM,eAAe,OAAO,QAAQ;EAEpC,cAAc,KAAK,+BAA+B;GAChD;GACA,UAAU,QAAQ;GAClB;GACA;GACA;GACA;GACA,WAAW,KAAK,IAAI;EACtB,CAAC;EAED,OAAO,OAAO,6BAA6B,gBAAgB;GACzD,YAAY,CAAC,CAAC,OAAO;GACrB;EACF,CAAC;EAED,IAAI,OAAO,OAAO,MAAM,mBAAmB,YAAY,OAAO,OAAO,KAAK;EAC1E,MAAM,oBAAoB,YAAY,OAAO;GAC3C;GACA,OAAO,OAAO;EAChB,CAAC;EAED,OAAO;CACT,SAAS,OAAO;EACd,cAAc,MAAM;EACpB,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,IAAI,qBAAqB,OAAO,cAAc,MAAM,GAClD,MAAM,mBAAmB,YAAY,OAAO;GAC1C,QAAQ,mBAAmB,OAAO,cAAc,MAAM;GACtD;EACF,CAAC;OAED,MAAM,mBAAmB,YAAY,OAAO;GAC1C;GACA;EACF,CAAC;EAEH,OAAO,OAAO,6BAA6B;GACzC;GACA,QAAQ;EACV,CAAC;EACD,MAAM;CACR;AACF;;AAGA,SAAS,QAAQ,QAA2D;CAC1E,IAAI,OAAO,WAAW,YAAY,WAAW,MAAM,OAAO,KAAA;CAC1D,MAAM,QAAS,OAA+B;CAC9C,OAAO,OAAO,UAAU,YAAY,UAAU,OACzC,QACD,KAAA;AACN;;;;;;AAOA,gBAAgB,sBACd,SAC4B;CAC5B,MAAM,EACJ,SACA,MACA,WACA,OACA,OACA,cACA,OACA,aACE;CACJ,MAAM,QAAQ,QAAQ;CACtB,MAAM,SAAyB,mBAAmB,QAAQ,KAAK;CAE/D,OAAO,QAAQ,+BAA+B,QAAQ,QAAQ;EAC5D,UAAU,QAAQ;EAClB;EACA,QAAQ;CACV,CAAC;CAMD,MAAM,mBAAmB;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;EACvC,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;CAC/C;CAGA,IAAI,QAAQ,iBAAiB;EAC3B,OAAO,yBACL,SACA,QAAQ,gBAAgB,gBAAgB,GACxC,MACF;EACA;CACF;CAEA,IAAI;EAOF,OAAO,wBACJ,aACC,aAAa;GAAE,GAAG;GAAS,QAAQ;GAAO,OAAO,SAAS;EAAM,CAAC,GACnE;GACE,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;GACvC,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;EAC/C,CACF;CACF,SAAS,OAAO;EACd,OAAO,OAAO,6BAA6B;GACzC;GACA,QAAQ;EACV,CAAC;EACD,MAAM;CACR;AACF;;;;;;;;;;;;AAaA,gBAAgB,yBACd,SACA,QACA,QAC4B;CAC5B,MAAM,EAAE,SAAS,YAAY,iBAAiB;CAC9C,MAAM,QAAQ,wBAAwB;EACpC,WAAW,SAAS,WAAW;EAC/B,UAAU;EACV,UAAU,QAAQ;EAClB,OAAO,QAAQ;EACf;EACA,UAAU,QAAQ;EAClB,OAAO,QAAQ;EACf;CACF,CAAC;CAED,MAAM,mBAAmB,YAAY,KAAK;CAE1C,MAAM,YAAY,KAAK,IAAI;CAG3B,IAAI,UAAU;CACd,IAAI;EACF,WAAW,MAAM,SAAS,QAAQ;GAChC,IAAI,MAAM,SAAS,YAAY,MAAM,SAAS,qBAAqB;IACjE,MAAM,SAAS,MAAM,gCACnB,OACA,MAAM,KACR;IACA,MAAM,QAAQ,QAAQ,MAAM;IAG5B,IAAI,OAAO,MAAM,mBAAmB,YAAY,OAAO,KAAK;IAC5D,MAAM,oBAAoB,YAAY,OAAO;KAC3C,UAAU,KAAK,IAAI,IAAI;KACvB;IACF,CAAC;IACD,UAAU;IACV,MAAM;KAAE,GAAG;KAAO,OAAO;IAAO;IAChC;GACF;GACA,MAAM;EACR;EACA,IAAI,CAAC,SAAS;GACZ,MAAM,oBAAoB,YAAY,OAAO,EAC3C,UAAU,KAAK,IAAI,IAAI,UACzB,CAAC;GACD,UAAU;EACZ;CACF,SAAS,OAAO;EACd,UAAU;EACV,MAAM,mBAAmB,YAAY,OAAO;GAC1C;GACA,UAAU,KAAK,IAAI,IAAI;EACzB,CAAC;EACD,OAAO,OAAO,6BAA6B;GACzC;GACA,QAAQ;EACV,CAAC;EACD,MAAM;CACR,UAAU;EACR,IAAI,CAAC,SAKH,MAAM,mBAAmB,YAAY,OAAO;GAC1C,QAAQ;GACR,UAAU,KAAK,IAAI,IAAI;EACzB,CAAC;CAEL;AACF;;;;AASA,SAAgB,uBAId,SAC6C;CAC7C,OAAO;AACT"}
@@ -1,12 +1,15 @@
1
- export { chat, summarize, generateImage, generateAudio, generateVideo, getVideoJobStatus, generateSpeech, generateTranscription, } from './activities/index.js';
1
+ export { chat, summarize, rerank, generateImage, generateAudio, generateVideo, getVideoJobStatus, generateSpeech, generateTranscription, embed, } from './activities/index.js';
2
2
  export { createChatOptions } from './activities/chat/index.js';
3
3
  export { createSummarizeOptions } from './activities/summarize/index.js';
4
+ export { createRerankOptions } from './activities/rerank/index.js';
4
5
  export { createImageOptions } from './activities/generateImage/index.js';
5
6
  export { createAudioOptions } from './activities/generateAudio/index.js';
6
7
  export { createVideoOptions } from './activities/generateVideo/index.js';
7
8
  export { createSpeechOptions } from './activities/generateSpeech/index.js';
8
9
  export { createTranscriptionOptions } from './activities/generateTranscription/index.js';
9
- export type { AIAdapter, ImageAdapter, AnyImageAdapter, TextAdapter, AnyTextAdapter, AnySummarizeAdapter, SummarizeAdapter, AnyAudioAdapter, AudioAdapter, AnyTTSAdapter, TTSAdapter, AnyTranscriptionAdapter, TranscriptionAdapter, AnyVideoAdapter, VideoAdapter, } from './activities/index.js';
10
+ export { createEmbedOptions } from './activities/embed/index.js';
11
+ export type { AIAdapter, ImageAdapter, AnyImageAdapter, TextAdapter, AnyTextAdapter, AnySummarizeAdapter, SummarizeAdapter, AnyAudioAdapter, AudioAdapter, AnyTTSAdapter, TTSAdapter, AnyTranscriptionAdapter, TranscriptionAdapter, AnyVideoAdapter, VideoAdapter, AnyEmbeddingAdapter, EmbeddingAdapter, AnyRerankAdapter, RerankAdapter, } from './activities/index.js';
12
+ export { BaseRerankAdapter } from './activities/rerank/adapter.js';
10
13
  export { toolDefinition, type ToolDefinition, type ToolDefinitionInstance, type ToolDefinitionConfig, type ServerTool, type AnyServerTool, type ClientTool, type AnyClientTool, type InferToolName, type InferToolInput, type InferToolOutput, type ApprovalCapabilityOf, type ApprovalSchemaConfig, type ApprovalSchemaOf, type InputSchemaOf, type OutputSchemaOf, type NoSchema, } from './activities/chat/tools/tool-definition.js';
11
14
  export { hashSchemaInput, normalizeApprovalSchema, type NormalizedApprovalSchema, type NormalizedSchemaInput, } from './activities/chat/tools/approval-schema.js';
12
15
  export { canonicalInterruptJson, cloneAndDeepFreezeJson, digestInterruptJson, } from './interrupt-serialization.js';
@@ -43,6 +46,8 @@ export { firstSentence, renderLazyCatalogEntry, } from './activities/chat/tools/
43
46
  export { buildBaseUsage, type BaseUsageInput } from './utilities/usage.js';
44
47
  export { resolveMediaPrompt } from './utilities/media-prompt.js';
45
48
  export type { ResolvedMediaPrompt } from './utilities/media-prompt.js';
49
+ export { resolveEmbeddingInput, requireTextOnlyEmbeddingInput, countEmbeddingInputModalities, } from './utilities/embedding-input.js';
50
+ export type { ResolvedEmbeddingItem } from './utilities/embedding-input.js';
46
51
  export type { SystemPrompt, NormalizedSystemPrompt } from './system-prompts.js';
47
52
  export { normalizeSystemPrompts } from './system-prompts.js';
48
53
  export { detectImageMimeType } from './utils.js';