@ai-sdk/quiverai 2.0.47 → 2.0.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ import {
2
+ InvalidArgumentError,
3
+ type LanguageModelV4,
4
+ type LanguageModelV4CallOptions,
5
+ type LanguageModelV4StreamPart,
6
+ type SharedV4ProviderMetadata,
7
+ type SharedV4Warning,
8
+ } from '@ai-sdk/provider';
9
+ import {
10
+ createOpenResponses,
11
+ type OpenResponsesProviderSettings,
12
+ } from '@ai-sdk/open-responses';
13
+ import {
14
+ serializeModelOptions,
15
+ WORKFLOW_DESERIALIZE,
16
+ WORKFLOW_SERIALIZE,
17
+ } from '@ai-sdk/provider-utils';
18
+ import {
19
+ getQuiverAIResponseErrorMetadata,
20
+ quiveraiFailedResponseHandler,
21
+ } from './quiverai-error';
22
+
23
+ /** Applies QuiverAI's request policy while reusing the Open Responses transport. */
24
+ export class QuiverAILanguageModel implements LanguageModelV4 {
25
+ readonly specificationVersion = 'v4';
26
+
27
+ static [WORKFLOW_SERIALIZE](model: QuiverAILanguageModel) {
28
+ return serializeModelOptions({
29
+ modelId: model.modelId,
30
+ config: model.config,
31
+ });
32
+ }
33
+
34
+ static [WORKFLOW_DESERIALIZE](options: {
35
+ modelId: string;
36
+ config: OpenResponsesProviderSettings;
37
+ }) {
38
+ // Restore QuiverAI's handlers, which cannot cross workflow boundaries.
39
+ const config: OpenResponsesProviderSettings = {
40
+ ...options.config,
41
+ failedResponseHandler: quiveraiFailedResponseHandler,
42
+ getResponseErrorMetadata: getQuiverAIResponseErrorMetadata,
43
+ };
44
+
45
+ return new QuiverAILanguageModel(
46
+ createOpenResponses(config).languageModel(options.modelId),
47
+ config,
48
+ );
49
+ }
50
+
51
+ constructor(
52
+ private readonly model: LanguageModelV4,
53
+ private readonly config: OpenResponsesProviderSettings,
54
+ ) {}
55
+
56
+ get modelId() {
57
+ return this.model.modelId;
58
+ }
59
+
60
+ get provider() {
61
+ return this.model.provider;
62
+ }
63
+
64
+ get supportedUrls() {
65
+ return this.model.supportedUrls;
66
+ }
67
+
68
+ async doGenerate(options: LanguageModelV4CallOptions) {
69
+ const prepared = prepareQuiverAICall(options);
70
+ const result = await this.model.doGenerate(prepared.options);
71
+ return { ...result, warnings: [...prepared.warnings, ...result.warnings] };
72
+ }
73
+
74
+ async doStream(options: LanguageModelV4CallOptions) {
75
+ const prepared = prepareQuiverAICall(options);
76
+ const result = await this.model.doStream(prepared.options);
77
+ if (prepared.warnings.length === 0) return result;
78
+ return {
79
+ ...result,
80
+ stream: result.stream.pipeThrough(
81
+ new TransformStream<
82
+ LanguageModelV4StreamPart,
83
+ LanguageModelV4StreamPart
84
+ >({
85
+ transform(part, controller) {
86
+ controller.enqueue(
87
+ part.type === 'stream-start'
88
+ ? {
89
+ ...part,
90
+ warnings: [...prepared.warnings, ...part.warnings],
91
+ }
92
+ : part,
93
+ );
94
+ },
95
+ }),
96
+ ),
97
+ };
98
+ }
99
+ }
100
+
101
+ function prepareQuiverAICall(options: LanguageModelV4CallOptions): {
102
+ options: LanguageModelV4CallOptions;
103
+ warnings: SharedV4Warning[];
104
+ } {
105
+ const { reasoningEffort, reasoningSummary } =
106
+ options.providerOptions?.quiverai ?? {};
107
+ if (
108
+ reasoningEffort != null &&
109
+ (typeof reasoningEffort !== 'string' ||
110
+ !['low', 'medium', 'high', 'xhigh'].includes(reasoningEffort))
111
+ ) {
112
+ throw new InvalidArgumentError({
113
+ argument: 'providerOptions',
114
+ message: `Unsupported reasoning effort: ${reasoningEffort}`,
115
+ });
116
+ }
117
+ if (reasoningSummary != null && reasoningSummary !== 'auto') {
118
+ throw new InvalidArgumentError({
119
+ argument: 'providerOptions',
120
+ message: `Unsupported reasoning summary: ${reasoningSummary}`,
121
+ });
122
+ }
123
+
124
+ const warnings: SharedV4Warning[] = [];
125
+ const omitReasoning = options.reasoning === 'none' && reasoningEffort == null;
126
+ if (omitReasoning) {
127
+ warnings.push({ type: 'unsupported', feature: 'reasoning effort none' });
128
+ }
129
+
130
+ return {
131
+ warnings,
132
+ options: {
133
+ ...options,
134
+ reasoning: omitReasoning ? undefined : options.reasoning,
135
+ prompt: options.prompt.map(message =>
136
+ message.role !== 'assistant'
137
+ ? message
138
+ : {
139
+ ...message,
140
+ content: message.content.map(part => {
141
+ if (part.type !== 'reasoning') return part;
142
+ const metadata =
143
+ part.providerOptions?.quiverai ??
144
+ (part as { providerMetadata?: SharedV4ProviderMetadata })
145
+ .providerMetadata?.quiverai;
146
+ return {
147
+ ...part,
148
+ text: '',
149
+ providerOptions: {
150
+ ...part.providerOptions,
151
+ quiverai: {
152
+ itemId: metadata?.itemId,
153
+ reasoningSummary: metadata?.reasoningSummary,
154
+ },
155
+ },
156
+ };
157
+ }),
158
+ },
159
+ ),
160
+ },
161
+ };
162
+ }
@@ -1,8 +1,13 @@
1
1
  import {
2
2
  NoSuchModelError,
3
3
  type ImageModelV4,
4
+ type LanguageModelV4,
4
5
  type ProviderV4,
5
6
  } from '@ai-sdk/provider';
7
+ import {
8
+ createOpenResponses,
9
+ type OpenResponsesProviderSettings,
10
+ } from '@ai-sdk/open-responses';
6
11
  import {
7
12
  loadApiKey,
8
13
  loadOptionalSetting,
@@ -10,8 +15,14 @@ import {
10
15
  withUserAgentSuffix,
11
16
  type FetchFunction,
12
17
  } from '@ai-sdk/provider-utils';
18
+ import {
19
+ getQuiverAIResponseErrorMetadata,
20
+ quiveraiFailedResponseHandler,
21
+ } from './quiverai-error';
22
+ import { QuiverAILanguageModel } from './quiverai-language-model';
13
23
  import { QuiverAIImageModel } from './quiverai-image-model';
14
24
  import type { QuiverAIImageModelId } from './quiverai-image-settings';
25
+ import type { QuiverAILanguageModelId } from './quiverai-language-model-settings';
15
26
  import { VERSION } from './version';
16
27
 
17
28
  export interface QuiverAIProviderSettings {
@@ -40,6 +51,16 @@ export interface QuiverAIProviderSettings {
40
51
  }
41
52
 
42
53
  export interface QuiverAIProvider extends ProviderV4 {
54
+ /**
55
+ * Creates a language model for the QuiverAI Responses API.
56
+ */
57
+ (modelId: QuiverAILanguageModelId): LanguageModelV4;
58
+
59
+ /**
60
+ * Creates a language model for the QuiverAI Responses API.
61
+ */
62
+ languageModel(modelId: QuiverAILanguageModelId): LanguageModelV4;
63
+
43
64
  /**
44
65
  * Creates a model for image generation.
45
66
  */
@@ -54,6 +75,11 @@ export interface QuiverAIProvider extends ProviderV4 {
54
75
  * @deprecated Use `embeddingModel` instead.
55
76
  */
56
77
  textEmbeddingModel(modelId: string): never;
78
+
79
+ /**
80
+ * QuiverAI caller-executed tools.
81
+ */
82
+ tools: ReturnType<typeof createOpenResponses>['tools'];
57
83
  }
58
84
 
59
85
  const defaultBaseURL = 'https://api.quiver.ai/v1';
@@ -69,24 +95,43 @@ export function createQuiverAI(
69
95
  }),
70
96
  ) ?? defaultBaseURL;
71
97
 
72
- const getHeaders = () =>
73
- withUserAgentSuffix(
74
- {
75
- Authorization: `Bearer ${loadApiKey({
76
- apiKey: options.apiKey,
77
- environmentVariableName: 'QUIVERAI_API_KEY',
78
- description: 'QuiverAI',
79
- })}`,
80
- ...options.headers,
81
- },
82
- `ai-sdk/quiverai/${VERSION}`,
98
+ const getHeaders = () => ({
99
+ Authorization: `Bearer ${loadApiKey({
100
+ apiKey: options.apiKey,
101
+ environmentVariableName: 'QUIVERAI_API_KEY',
102
+ description: 'QuiverAI',
103
+ })}`,
104
+ ...options.headers,
105
+ });
106
+
107
+ const getImageHeaders = () =>
108
+ withUserAgentSuffix(getHeaders(), `ai-sdk/quiverai/${VERSION}`);
109
+
110
+ const responsesConfig: OpenResponsesProviderSettings = {
111
+ name: 'quiverai',
112
+ url: `${baseURL}/responses`,
113
+ headers: getHeaders,
114
+ fetch: options.fetch,
115
+ failedResponseHandler: quiveraiFailedResponseHandler,
116
+ getResponseErrorMetadata: getQuiverAIResponseErrorMetadata,
117
+ strictResponseInput: true,
118
+ customToolId: 'quiverai.custom',
119
+ structuredOutputs: false,
120
+ userAgentSuffix: `ai-sdk/quiverai/${VERSION}`,
121
+ };
122
+ const responsesProvider = createOpenResponses(responsesConfig);
123
+
124
+ const createLanguageModel = (modelId: QuiverAILanguageModelId) =>
125
+ new QuiverAILanguageModel(
126
+ responsesProvider.languageModel(modelId),
127
+ responsesConfig,
83
128
  );
84
129
 
85
130
  const createImageModel = (modelId: QuiverAIImageModelId) =>
86
131
  new QuiverAIImageModel(modelId, {
87
132
  provider: 'quiverai.image',
88
133
  baseURL,
89
- headers: getHeaders,
134
+ headers: getImageHeaders,
90
135
  fetch: options.fetch,
91
136
  });
92
137
 
@@ -97,19 +142,19 @@ export function createQuiverAI(
97
142
  });
98
143
  };
99
144
 
100
- return {
101
- specificationVersion: 'v4',
102
- image: createImageModel,
103
- imageModel: createImageModel,
104
- languageModel: (modelId: string) => {
105
- throw new NoSuchModelError({
106
- modelId,
107
- modelType: 'languageModel',
108
- });
109
- },
110
- embeddingModel,
111
- textEmbeddingModel: embeddingModel,
145
+ const provider = function (modelId: QuiverAILanguageModelId) {
146
+ return createLanguageModel(modelId);
112
147
  };
148
+
149
+ provider.specificationVersion = 'v4' as const;
150
+ provider.languageModel = createLanguageModel;
151
+ provider.image = createImageModel;
152
+ provider.imageModel = createImageModel;
153
+ provider.embeddingModel = embeddingModel;
154
+ provider.textEmbeddingModel = embeddingModel;
155
+ provider.tools = responsesProvider.tools;
156
+
157
+ return provider as QuiverAIProvider;
113
158
  }
114
159
 
115
160
  export const quiverai = createQuiverAI();