@tanstack/ai-lovable 0.2.10 → 0.3.1
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.
- package/dist/esm/adapters/responses-text.d.ts +2 -2
- package/dist/esm/adapters/responses-text.js +1 -1
- package/dist/esm/adapters/responses-text.js.map +1 -1
- package/dist/esm/adapters/text.d.ts +2 -2
- package/dist/esm/adapters/text.js +1 -1
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/image/image-input-to-file.js +2 -0
- package/dist/esm/image/image-input-to-file.js.map +1 -1
- package/package.json +5 -4
- package/src/adapters/responses-text.ts +4 -2
- package/src/adapters/text.ts +4 -2
- package/src/image/image-input-to-file.ts +10 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base';
|
|
1
|
+
import { OpenAIBaseResponsesTextAdapter, OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base';
|
|
2
2
|
import { Modality } from '@tanstack/ai';
|
|
3
3
|
import { LovableChatModelToolCapabilitiesByName, LovableModelId, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
|
|
4
4
|
import { LovableMessageMetadataByModality } from '../message-types.js';
|
|
5
5
|
import { ExternalResponsesProviderOptions } from '../text/responses-provider-options.js';
|
|
6
6
|
import { LovableClientConfig } from '../utils/client.js';
|
|
7
|
-
export interface LovableResponsesTextConfig extends LovableClientConfig {
|
|
7
|
+
export interface LovableResponsesTextConfig extends LovableClientConfig, OpenAIBaseTextAdapterOptions {
|
|
8
8
|
}
|
|
9
9
|
export type LovableResponsesTextProviderOptions = ExternalResponsesProviderOptions;
|
|
10
10
|
type ResolveToolCapabilities<TModel extends string> = TModel extends keyof LovableChatModelToolCapabilitiesByName ? NonNullable<LovableChatModelToolCapabilitiesByName[TModel]> : readonly [];
|
|
@@ -12,7 +12,7 @@ var LovableResponsesTextAdapter = class extends OpenAIBaseResponsesTextAdapter {
|
|
|
12
12
|
kind = "text";
|
|
13
13
|
name = "lovable";
|
|
14
14
|
constructor(config, model) {
|
|
15
|
-
super(model, "lovable", new OpenAI(withLovableDefaults(config)));
|
|
15
|
+
super(model, "lovable", new OpenAI(withLovableDefaults(config)), config);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
function createLovableResponsesText(model, apiKey, config) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responses-text.js","names":[],"sources":["../../../src/adapters/responses-text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'\nimport { getLovableApiKeyFromEnv, withLovableDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type {\n LovableChatModelToolCapabilitiesByName,\n LovableModelId,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { LovableMessageMetadataByModality } from '../message-types'\nimport type { ExternalResponsesProviderOptions } from '../text/responses-provider-options'\nimport type { LovableClientConfig } from '../utils/client'\n\nexport interface LovableResponsesTextConfig
|
|
1
|
+
{"version":3,"file":"responses-text.js","names":[],"sources":["../../../src/adapters/responses-text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseResponsesTextAdapter } from '@tanstack/openai-base'\nimport { getLovableApiKeyFromEnv, withLovableDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type {\n LovableChatModelToolCapabilitiesByName,\n LovableModelId,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { LovableMessageMetadataByModality } from '../message-types'\nimport type { ExternalResponsesProviderOptions } from '../text/responses-provider-options'\nimport type { LovableClientConfig } from '../utils/client'\nimport type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'\n\nexport interface LovableResponsesTextConfig\n extends LovableClientConfig, OpenAIBaseTextAdapterOptions {}\n\nexport type LovableResponsesTextProviderOptions =\n ExternalResponsesProviderOptions\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof LovableChatModelToolCapabilitiesByName\n ? NonNullable<LovableChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\n/**\n * Lovable AI Gateway Responses text adapter.\n *\n * Talks to the OpenAI-compatible Responses API at\n * `https://ai.gateway.lovable.dev/v1`.\n */\nexport class LovableResponsesTextAdapter<\n TModel extends LovableModelId,\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseResponsesTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n LovableMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'lovable' as const\n\n constructor(config: LovableResponsesTextConfig, model: TModel) {\n super(model, 'lovable', new OpenAI(withLovableDefaults(config)), config)\n }\n}\n\nexport function createLovableResponsesText<TModel extends LovableModelId>(\n model: TModel,\n apiKey: string,\n config?: Omit<LovableResponsesTextConfig, 'apiKey'>,\n): LovableResponsesTextAdapter<TModel> {\n return new LovableResponsesTextAdapter({ apiKey, ...config }, model)\n}\n\nexport function lovableResponsesText<TModel extends LovableModelId>(\n model: TModel,\n config?: Omit<LovableResponsesTextConfig, 'apiKey'>,\n): LovableResponsesTextAdapter<TModel> {\n return createLovableResponsesText(model, getLovableApiKeyFromEnv(), config)\n}\n"],"mappings":";;;;;;;;;;AAgCA,IAAa,8BAAb,cAOU,+BAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAAoC,OAAe;EAC7D,MAAM,OAAO,WAAW,IAAI,OAAO,oBAAoB,MAAM,CAAC,GAAG,MAAM;CACzE;AACF;AAEA,SAAgB,2BACd,OACA,QACA,QACqC;CACrC,OAAO,IAAI,4BAA4B;EAAE;EAAQ,GAAG;CAAO,GAAG,KAAK;AACrE;AAEA,SAAgB,qBACd,OACA,QACqC;CACrC,OAAO,2BAA2B,OAAO,wBAAwB,GAAG,MAAM;AAC5E"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base';
|
|
1
|
+
import { OpenAIBaseChatCompletionsTextAdapter, OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base';
|
|
2
2
|
import { Modality } from '@tanstack/ai';
|
|
3
3
|
import { LovableChatModelToolCapabilitiesByName, LovableModelId, ResolveInputModalities, ResolveProviderOptions } from '../model-meta.js';
|
|
4
4
|
import { LovableMessageMetadataByModality } from '../message-types.js';
|
|
5
5
|
import { LovableClientConfig } from '../utils/client.js';
|
|
6
6
|
type ResolveToolCapabilities<TModel extends string> = TModel extends keyof LovableChatModelToolCapabilitiesByName ? NonNullable<LovableChatModelToolCapabilitiesByName[TModel]> : readonly [];
|
|
7
|
-
export interface LovableTextConfig extends LovableClientConfig {
|
|
7
|
+
export interface LovableTextConfig extends LovableClientConfig, OpenAIBaseTextAdapterOptions {
|
|
8
8
|
}
|
|
9
9
|
export type { ExternalTextProviderOptions as LovableTextProviderOptions } from '../text/text-provider-options.js';
|
|
10
10
|
/**
|
|
@@ -12,7 +12,7 @@ var LovableTextAdapter = class extends OpenAIBaseChatCompletionsTextAdapter {
|
|
|
12
12
|
kind = "text";
|
|
13
13
|
name = "lovable";
|
|
14
14
|
constructor(config, model) {
|
|
15
|
-
super(model, "lovable", new OpenAI(withLovableDefaults(config)));
|
|
15
|
+
super(model, "lovable", new OpenAI(withLovableDefaults(config)), config);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { withLovableDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type {\n LovableChatModelToolCapabilitiesByName,\n LovableModelId,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { LovableMessageMetadataByModality } from '../message-types'\nimport type { LovableClientConfig } from '../utils/client'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof LovableChatModelToolCapabilitiesByName\n ? NonNullable<LovableChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\nexport interface LovableTextConfig
|
|
1
|
+
{"version":3,"file":"text.js","names":[],"sources":["../../../src/adapters/text.ts"],"sourcesContent":["import OpenAI from 'openai'\nimport { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'\nimport { withLovableDefaults } from '../utils/client'\nimport type { Modality } from '@tanstack/ai'\nimport type {\n LovableChatModelToolCapabilitiesByName,\n LovableModelId,\n ResolveInputModalities,\n ResolveProviderOptions,\n} from '../model-meta'\nimport type { LovableMessageMetadataByModality } from '../message-types'\nimport type { LovableClientConfig } from '../utils/client'\nimport type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'\n\ntype ResolveToolCapabilities<TModel extends string> =\n TModel extends keyof LovableChatModelToolCapabilitiesByName\n ? NonNullable<LovableChatModelToolCapabilitiesByName[TModel]>\n : readonly []\n\nexport interface LovableTextConfig\n extends LovableClientConfig, OpenAIBaseTextAdapterOptions {}\n\nexport type { ExternalTextProviderOptions as LovableTextProviderOptions } from '../text/text-provider-options'\n\n/**\n * Lovable AI Gateway text adapter.\n *\n * Talks to the OpenAI-compatible Chat Completions API at\n * `https://ai.gateway.lovable.dev/v1`.\n */\nexport class LovableTextAdapter<\n TModel extends LovableModelId,\n TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,\n TInputModalities extends ReadonlyArray<Modality> =\n ResolveInputModalities<TModel>,\n TToolCapabilities extends ReadonlyArray<string> =\n ResolveToolCapabilities<TModel>,\n> extends OpenAIBaseChatCompletionsTextAdapter<\n TModel,\n TProviderOptions,\n TInputModalities,\n LovableMessageMetadataByModality,\n TToolCapabilities\n> {\n override readonly kind = 'text' as const\n override readonly name = 'lovable' as const\n\n constructor(config: LovableTextConfig, model: TModel) {\n super(model, 'lovable', new OpenAI(withLovableDefaults(config)), config)\n }\n}\n"],"mappings":";;;;;;;;;;AA8BA,IAAa,qBAAb,cAOU,qCAMR;CACA,OAAyB;CACzB,OAAyB;CAEzB,YAAY,QAA2B,OAAe;EACpD,MAAM,OAAO,WAAW,IAAI,OAAO,oBAAoB,MAAM,CAAC,GAAG,MAAM;CACzE;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { base64ToArrayBuffer } from "@tanstack/ai-utils";
|
|
2
|
+
import { isFileSource, unsupportedFileSourceError } from "@tanstack/ai";
|
|
2
3
|
//#region src/image/image-input-to-file.ts
|
|
3
4
|
var DEFAULT_MIME = "image/png";
|
|
4
5
|
var MIME_TO_EXT = {
|
|
@@ -22,6 +23,7 @@ function ensureFileSupport() {
|
|
|
22
23
|
*/
|
|
23
24
|
async function imagePartToFile(part, fallbackName, allowUrlFetch, abortSignal) {
|
|
24
25
|
ensureFileSupport();
|
|
26
|
+
if (isFileSource(part.source)) throw unsupportedFileSourceError("lovable", "on the edit / input_reference endpoints, which require uploaded bytes — pass a data: URI or inline image data");
|
|
25
27
|
if (part.source.type === "data") {
|
|
26
28
|
const mimeType = part.source.mimeType || DEFAULT_MIME;
|
|
27
29
|
const bytes = base64ToArrayBuffer(part.source.value);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-input-to-file.js","names":[],"sources":["../../../src/image/image-input-to-file.ts"],"sourcesContent":["import { base64ToArrayBuffer } from '@tanstack/ai-utils'\nimport type { ImagePart, MediaInputMetadata } from '@tanstack/ai'\n\nconst DEFAULT_MIME = 'image/png'\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/jpg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n}\n\nfunction extForMime(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? mimeType.split('/')[1] ?? 'png'\n}\n\nfunction ensureFileSupport(): void {\n if (typeof File === 'undefined') {\n throw new Error(\n '`File` is not available in this environment. ' +\n 'Image-conditioned generation requires Node 20+ or a browser context.',\n )\n }\n}\n\n/**\n * Convert a TanStack `ImagePart` into an OpenAI-compatible `File`.\n *\n * HTTP(S) URLs are fetched only when `allowUrlFetch` is set. The gateway\n * edit and `input_reference` endpoints need uploaded bytes, not a URL.\n */\nexport async function imagePartToFile(\n part: ImagePart<MediaInputMetadata>,\n fallbackName: string,\n allowUrlFetch: boolean,\n abortSignal?: AbortSignal,\n): Promise<File> {\n ensureFileSupport()\n\n if (part.source.type === 'data') {\n const mimeType = part.source.mimeType || DEFAULT_MIME\n const bytes = base64ToArrayBuffer(part.source.value)\n return new File([bytes], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n }\n\n if (/^https?:\\/\\//i.test(part.source.value) && !allowUrlFetch) {\n throw new Error(\n `lovable: HTTP(S) URL image inputs are not fetched by default because ` +\n `the edit / input_reference endpoints require uploaded bytes, so ` +\n `the image would be downloaded and buffered in memory (risking OOM on ` +\n `constrained runtimes). Pass a data: URI, or set \\`allowUrlFetch: true\\` ` +\n `on the adapter config to opt into fetching. URL: ${part.source.value}`,\n )\n }\n\n const response = await fetch(\n part.source.value,\n abortSignal ? { signal: abortSignal } : undefined,\n )\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image input (${response.status} ${response.statusText}): ${part.source.value}`,\n )\n }\n const blob = await response.blob()\n const mimeType =\n part.source.mimeType || blob.type || inferMimeFromUrl(part.source.value)\n return new File([blob], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n}\n\nfunction inferMimeFromUrl(url: string): string {\n const match = url.match(/\\.(png|jpe?g|webp|gif)(?:\\?|#|$)/i)\n if (!match || !match[1]) return DEFAULT_MIME\n const ext = match[1].toLowerCase()\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n return `image/${ext}`\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"image-input-to-file.js","names":[],"sources":["../../../src/image/image-input-to-file.ts"],"sourcesContent":["import { base64ToArrayBuffer } from '@tanstack/ai-utils'\nimport { isFileSource, unsupportedFileSourceError } from '@tanstack/ai'\nimport type { ImagePart, MediaInputMetadata } from '@tanstack/ai'\n\nconst DEFAULT_MIME = 'image/png'\nconst MIME_TO_EXT: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/jpg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n}\n\nfunction extForMime(mimeType: string): string {\n return MIME_TO_EXT[mimeType] ?? mimeType.split('/')[1] ?? 'png'\n}\n\nfunction ensureFileSupport(): void {\n if (typeof File === 'undefined') {\n throw new Error(\n '`File` is not available in this environment. ' +\n 'Image-conditioned generation requires Node 20+ or a browser context.',\n )\n }\n}\n\n/**\n * Convert a TanStack `ImagePart` into an OpenAI-compatible `File`.\n *\n * HTTP(S) URLs are fetched only when `allowUrlFetch` is set. The gateway\n * edit and `input_reference` endpoints need uploaded bytes, not a URL.\n */\nexport async function imagePartToFile(\n part: ImagePart<MediaInputMetadata>,\n fallbackName: string,\n allowUrlFetch: boolean,\n abortSignal?: AbortSignal,\n): Promise<File> {\n ensureFileSupport()\n\n if (isFileSource(part.source)) {\n // These endpoints are multipart and require the actual bytes; there is no\n // \"reference an uploaded handle\" option, and lovable has no Files API.\n throw unsupportedFileSourceError(\n 'lovable',\n 'on the edit / input_reference endpoints, which require uploaded bytes — pass a data: URI or inline image data',\n )\n }\n\n if (part.source.type === 'data') {\n const mimeType = part.source.mimeType || DEFAULT_MIME\n const bytes = base64ToArrayBuffer(part.source.value)\n return new File([bytes], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n }\n\n if (/^https?:\\/\\//i.test(part.source.value) && !allowUrlFetch) {\n throw new Error(\n `lovable: HTTP(S) URL image inputs are not fetched by default because ` +\n `the edit / input_reference endpoints require uploaded bytes, so ` +\n `the image would be downloaded and buffered in memory (risking OOM on ` +\n `constrained runtimes). Pass a data: URI, or set \\`allowUrlFetch: true\\` ` +\n `on the adapter config to opt into fetching. URL: ${part.source.value}`,\n )\n }\n\n const response = await fetch(\n part.source.value,\n abortSignal ? { signal: abortSignal } : undefined,\n )\n if (!response.ok) {\n throw new Error(\n `Failed to fetch image input (${response.status} ${response.statusText}): ${part.source.value}`,\n )\n }\n const blob = await response.blob()\n const mimeType =\n part.source.mimeType || blob.type || inferMimeFromUrl(part.source.value)\n return new File([blob], `${fallbackName}.${extForMime(mimeType)}`, {\n type: mimeType,\n })\n}\n\nfunction inferMimeFromUrl(url: string): string {\n const match = url.match(/\\.(png|jpe?g|webp|gif)(?:\\?|#|$)/i)\n if (!match || !match[1]) return DEFAULT_MIME\n const ext = match[1].toLowerCase()\n if (ext === 'jpg' || ext === 'jpeg') return 'image/jpeg'\n return `image/${ext}`\n}\n"],"mappings":";;;AAIA,IAAM,eAAe;AACrB,IAAM,cAAsC;CAC1C,aAAa;CACb,cAAc;CACd,aAAa;CACb,cAAc;CACd,aAAa;AACf;AAEA,SAAS,WAAW,UAA0B;CAC5C,OAAO,YAAY,aAAa,SAAS,MAAM,GAAG,CAAC,CAAC,MAAM;AAC5D;AAEA,SAAS,oBAA0B;CACjC,IAAI,OAAO,SAAS,aAClB,MAAM,IAAI,MACR,mHAEF;AAEJ;;;;;;;AAQA,eAAsB,gBACpB,MACA,cACA,eACA,aACe;CACf,kBAAkB;CAElB,IAAI,aAAa,KAAK,MAAM,GAG1B,MAAM,2BACJ,WACA,+GACF;CAGF,IAAI,KAAK,OAAO,SAAS,QAAQ;EAC/B,MAAM,WAAW,KAAK,OAAO,YAAY;EACzC,MAAM,QAAQ,oBAAoB,KAAK,OAAO,KAAK;EACnD,OAAO,IAAI,KAAK,CAAC,KAAK,GAAG,GAAG,aAAa,GAAG,WAAW,QAAQ,KAAK,EAClE,MAAM,SACR,CAAC;CACH;CAEA,IAAI,gBAAgB,KAAK,KAAK,OAAO,KAAK,KAAK,CAAC,eAC9C,MAAM,IAAI,MACR,sUAIsD,KAAK,OAAO,OACpE;CAGF,MAAM,WAAW,MAAM,MACrB,KAAK,OAAO,OACZ,cAAc,EAAE,QAAQ,YAAY,IAAI,KAAA,CAC1C;CACA,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,gCAAgC,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,KAAK,OAAO,OAC1F;CAEF,MAAM,OAAO,MAAM,SAAS,KAAK;CACjC,MAAM,WACJ,KAAK,OAAO,YAAY,KAAK,QAAQ,iBAAiB,KAAK,OAAO,KAAK;CACzE,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,GAAG,aAAa,GAAG,WAAW,QAAQ,KAAK,EACjE,MAAM,SACR,CAAC;AACH;AAEA,SAAS,iBAAiB,KAAqB;CAC7C,MAAM,QAAQ,IAAI,MAAM,mCAAmC;CAC3D,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,OAAO;CAChC,MAAM,MAAM,MAAM,EAAE,CAAC,YAAY;CACjC,IAAI,QAAQ,SAAS,QAAQ,QAAQ,OAAO;CAC5C,OAAO,SAAS;AAClB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-lovable",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Lovable AI Gateway adapter for TanStack AI chat, embeddings, image, video, speech, and transcription.",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@vitest/coverage-v8": "4.1.10",
|
|
56
56
|
"vite": "^8.2.1",
|
|
57
|
-
"@tanstack/ai": "0.
|
|
57
|
+
"@tanstack/ai": "0.63.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@tanstack/ai": "^0.
|
|
60
|
+
"@tanstack/ai": "^0.63.0"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"openai": "^6.41.0",
|
|
64
64
|
"@tanstack/ai-utils": "^0.4.1",
|
|
65
|
-
"@tanstack/openai-base": "^0.
|
|
65
|
+
"@tanstack/openai-base": "^0.12.1"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "vite build",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"lint:fix": "oxlint src --type-aware --fix",
|
|
71
71
|
"test:build": "publint --strict",
|
|
72
72
|
"test:oxlint": "oxlint src --type-aware",
|
|
73
|
+
"test:coverage": "vitest run --coverage --coverage.include='src/**' --coverage.reporter=text-summary --coverage.reporter=json-summary",
|
|
73
74
|
"test:lib": "vitest run",
|
|
74
75
|
"test:lib:dev": "pnpm test:lib --watch",
|
|
75
76
|
"test:types": "tsc"
|
|
@@ -11,8 +11,10 @@ import type {
|
|
|
11
11
|
import type { LovableMessageMetadataByModality } from '../message-types'
|
|
12
12
|
import type { ExternalResponsesProviderOptions } from '../text/responses-provider-options'
|
|
13
13
|
import type { LovableClientConfig } from '../utils/client'
|
|
14
|
+
import type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'
|
|
14
15
|
|
|
15
|
-
export interface LovableResponsesTextConfig
|
|
16
|
+
export interface LovableResponsesTextConfig
|
|
17
|
+
extends LovableClientConfig, OpenAIBaseTextAdapterOptions {}
|
|
16
18
|
|
|
17
19
|
export type LovableResponsesTextProviderOptions =
|
|
18
20
|
ExternalResponsesProviderOptions
|
|
@@ -46,7 +48,7 @@ export class LovableResponsesTextAdapter<
|
|
|
46
48
|
override readonly name = 'lovable' as const
|
|
47
49
|
|
|
48
50
|
constructor(config: LovableResponsesTextConfig, model: TModel) {
|
|
49
|
-
super(model, 'lovable', new OpenAI(withLovableDefaults(config)))
|
|
51
|
+
super(model, 'lovable', new OpenAI(withLovableDefaults(config)), config)
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
package/src/adapters/text.ts
CHANGED
|
@@ -10,13 +10,15 @@ import type {
|
|
|
10
10
|
} from '../model-meta'
|
|
11
11
|
import type { LovableMessageMetadataByModality } from '../message-types'
|
|
12
12
|
import type { LovableClientConfig } from '../utils/client'
|
|
13
|
+
import type { OpenAIBaseTextAdapterOptions } from '@tanstack/openai-base'
|
|
13
14
|
|
|
14
15
|
type ResolveToolCapabilities<TModel extends string> =
|
|
15
16
|
TModel extends keyof LovableChatModelToolCapabilitiesByName
|
|
16
17
|
? NonNullable<LovableChatModelToolCapabilitiesByName[TModel]>
|
|
17
18
|
: readonly []
|
|
18
19
|
|
|
19
|
-
export interface LovableTextConfig
|
|
20
|
+
export interface LovableTextConfig
|
|
21
|
+
extends LovableClientConfig, OpenAIBaseTextAdapterOptions {}
|
|
20
22
|
|
|
21
23
|
export type { ExternalTextProviderOptions as LovableTextProviderOptions } from '../text/text-provider-options'
|
|
22
24
|
|
|
@@ -44,6 +46,6 @@ export class LovableTextAdapter<
|
|
|
44
46
|
override readonly name = 'lovable' as const
|
|
45
47
|
|
|
46
48
|
constructor(config: LovableTextConfig, model: TModel) {
|
|
47
|
-
super(model, 'lovable', new OpenAI(withLovableDefaults(config)))
|
|
49
|
+
super(model, 'lovable', new OpenAI(withLovableDefaults(config)), config)
|
|
48
50
|
}
|
|
49
51
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { base64ToArrayBuffer } from '@tanstack/ai-utils'
|
|
2
|
+
import { isFileSource, unsupportedFileSourceError } from '@tanstack/ai'
|
|
2
3
|
import type { ImagePart, MediaInputMetadata } from '@tanstack/ai'
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MIME = 'image/png'
|
|
@@ -37,6 +38,15 @@ export async function imagePartToFile(
|
|
|
37
38
|
): Promise<File> {
|
|
38
39
|
ensureFileSupport()
|
|
39
40
|
|
|
41
|
+
if (isFileSource(part.source)) {
|
|
42
|
+
// These endpoints are multipart and require the actual bytes; there is no
|
|
43
|
+
// "reference an uploaded handle" option, and lovable has no Files API.
|
|
44
|
+
throw unsupportedFileSourceError(
|
|
45
|
+
'lovable',
|
|
46
|
+
'on the edit / input_reference endpoints, which require uploaded bytes — pass a data: URI or inline image data',
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
if (part.source.type === 'data') {
|
|
41
51
|
const mimeType = part.source.mimeType || DEFAULT_MIME
|
|
42
52
|
const bytes = base64ToArrayBuffer(part.source.value)
|