diff --git a/cspell.config.yaml b/cspell.config.yaml index 4dcd1b6d..0752c7af 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -1,4 +1,4 @@ -version: '0.2' +version: "0.2" ignorePaths: [] dictionaryDefinitions: [] dictionaries: [] @@ -13,6 +13,7 @@ words: - gcornut - importantimport - koemotion + - masknet - Moeru - moeru-ai - mxbai-embed-large @@ -22,6 +23,7 @@ words: - Ollama - pkgroll - qwen + - sonarjs - twoslash - typeschema - unspeech diff --git a/packages/generate-object/package.json b/packages/generate-object/package.json index 64d67e5b..a74c8a4f 100644 --- a/packages/generate-object/package.json +++ b/packages/generate-object/package.json @@ -33,8 +33,7 @@ }, "dependencies": { "@typeschema/main": "catalog:", - "@xsai/generate-text": "workspace:", - "@xsai/shared": "workspace:" + "@xsai/generate-text": "workspace:" }, "devDependencies": { "@gcornut/valibot-json-schema": "catalog:", diff --git a/packages/generate-object/src/index.ts b/packages/generate-object/src/index.ts index 2b3b6603..1fd0b63a 100644 --- a/packages/generate-object/src/index.ts +++ b/packages/generate-object/src/index.ts @@ -1,6 +1,5 @@ import { type Infer, type Schema, toJSONSchema, validate } from '@typeschema/main' import { generateText, type GenerateTextOptions, type GenerateTextResult } from '@xsai/generate-text' -import { clean } from '@xsai/shared' export interface GenerateObjectOptions extends GenerateTextOptions { schema: T @@ -15,17 +14,14 @@ export interface GenerateObjectResult extends Omit(options: GenerateObjectOptions): Promise> => + // eslint-disable-next-line @masknet/no-then generateText({ ...options, response_format: { json_schema: { description: options.schemaDescription, name: options.schemaName ?? 'json_schema', - schema: await toJSONSchema(options.schema) - .then(json => clean({ - ...json, - $schema: undefined, - })), + schema: await toJSONSchema(options.schema), strict: true, }, type: 'json_schema', diff --git a/packages/generate-object/test/index.test.ts b/packages/generate-object/test/index.test.ts index f3a80fed..1ad3db7e 100644 --- a/packages/generate-object/test/index.test.ts +++ b/packages/generate-object/test/index.test.ts @@ -4,6 +4,7 @@ import { describe, expect, it } from 'vitest' import { generateObject } from '../src' +// eslint-disable-next-line @masknet/no-top-level describe('@xsai/generate-object', () => { it('basic', async () => { const { object } = await generateObject({ diff --git a/packages/generate-speech/test/index.test.ts b/packages/generate-speech/test/index.test.ts index fb38c257..4ec6144a 100644 --- a/packages/generate-speech/test/index.test.ts +++ b/packages/generate-speech/test/index.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest' import { generateSpeech } from '../src' +// eslint-disable-next-line @masknet/no-top-level describe('@xsai/generate-speech', () => { it('basic', async () => { const speech = await generateSpeech({ @@ -17,6 +18,7 @@ describe('@xsai/generate-speech', () => { it('chinese', async () => { const speech = await generateSpeech({ baseURL: new URL('http://localhost:5050'), + // eslint-disable-next-line @masknet/unicode-specific-set input: '我能吞下玻璃而不伤身体。', model: 'tts-1', voice: 'zh-CN-XiaoyiNeural', diff --git a/packages/generate-text/src/index.ts b/packages/generate-text/src/index.ts index 8d68da3a..dd3a543c 100644 --- a/packages/generate-text/src/index.ts +++ b/packages/generate-text/src/index.ts @@ -72,6 +72,7 @@ type RawGenerateTextTrampoline = Promise<(() => RawGenerateTextTrampoline) /** @internal */ const rawGenerateText: RawGenerateText = async (options: GenerateTextOptions) => + // eslint-disable-next-line @masknet/no-then chat({ ...options, maxSteps: undefined, @@ -88,7 +89,7 @@ const rawGenerateText: RawGenerateText = async (options: GenerateTextOptions) => const { finish_reason: finishReason, message } = choices[0] - if (message.content || !message.tool_calls || steps.length >= (options.maxSteps ?? 1)) { + if (message.content !== undefined || !message.tool_calls || steps.length >= (options.maxSteps ?? 1)) { const step: StepResult = { text: message.content, toolCalls, @@ -116,7 +117,7 @@ const rawGenerateText: RawGenerateText = async (options: GenerateTextOptions) => type: toolCallType, } of message.tool_calls) { const tool = (options.tools as Tool[]).find(tool => tool.function.name === toolName)! - const parsedArgs: Record = JSON.parse(toolArgs) + const parsedArgs = JSON.parse(toolArgs) as Record const result = await tool.execute(parsedArgs) toolCalls.push({ diff --git a/packages/generate-transcription/src/index.ts b/packages/generate-transcription/src/index.ts index bcee1c36..25eca785 100644 --- a/packages/generate-transcription/src/index.ts +++ b/packages/generate-transcription/src/index.ts @@ -24,6 +24,7 @@ export const generateTranscription = async (options: GenerateTranscriptionOption body.append('model', options.model) body.append('file', options.file, options.fileName) + // eslint-disable-next-line @masknet/no-then return (options.fetch ?? globalThis.fetch)(requestURL('audio/transcriptions', options.baseURL), { body, headers: requestHeaders(options.headers, options.apiKey), diff --git a/packages/generate-transcription/test/index.test.ts b/packages/generate-transcription/test/index.test.ts index 829fb125..a0666ff2 100644 --- a/packages/generate-transcription/test/index.test.ts +++ b/packages/generate-transcription/test/index.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest' import { generateTranscription } from '../src' +// eslint-disable-next-line @masknet/no-top-level describe('@xsai/generate-transcription', () => { it('basic', async () => { const { text } = await generateTranscription({ diff --git a/packages/model/src/utils/list-models.ts b/packages/model/src/utils/list-models.ts index f4824b01..8ed38f34 100644 --- a/packages/model/src/utils/list-models.ts +++ b/packages/model/src/utils/list-models.ts @@ -10,6 +10,7 @@ export interface ListModelsResponse { } export const listModels = async (options: ListModelsOptions): Promise => + // eslint-disable-next-line @masknet/no-then (options.fetch ?? globalThis.fetch)(requestURL('models', options.baseURL), { headers: requestHeaders({ 'Content-Type': 'application/json', diff --git a/packages/model/test/index.test.ts b/packages/model/test/index.test.ts index a71dbf96..5b42af12 100644 --- a/packages/model/test/index.test.ts +++ b/packages/model/test/index.test.ts @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest' import { listModels, retrieveModel } from '../src' +// eslint-disable-next-line @masknet/no-top-level describe('@xsai/model', () => { it('listModels', async () => { const models = await listModels({ diff --git a/packages/providers/src/providers/ollama.ts b/packages/providers/src/providers/ollama.ts index 891cd8f4..3ad6ad64 100644 --- a/packages/providers/src/providers/ollama.ts +++ b/packages/providers/src/providers/ollama.ts @@ -4,8 +4,8 @@ import { generateCRO } from '../utils/generate-cro' export const createOllama = (userOptions?: ProviderOptions): /** @see {@link https://ollama.com/search} */ - ChatProvider<'gemma2' | 'llama3.1' | 'llama3.2' | 'llama3.2-vision' | 'llama3.3' | 'qwen2.5' | 'qwen2.5-coder' | 'qwq' | (string & {})> - & EmbedProvider<'all-minilm' | 'mxbai-embed-large' | 'nomic-embed-text' | (string & {})> + ChatProvider<'gemma2' | 'llama3.1' | 'llama3.2' | 'llama3.2-vision' | 'llama3.3' | 'qwen2.5' | 'qwen2.5-coder' | 'qwq'> + & EmbedProvider<'all-minilm' | 'mxbai-embed-large' | 'nomic-embed-text'> & ModelProvider => { const options: ProviderResult = { ...userOptions, diff --git a/packages/providers/src/providers/unspeech/elevenlabs.ts b/packages/providers/src/providers/unspeech/elevenlabs.ts index 0f553836..85781e29 100644 --- a/packages/providers/src/providers/unspeech/elevenlabs.ts +++ b/packages/providers/src/providers/unspeech/elevenlabs.ts @@ -8,10 +8,10 @@ import { generateCRO } from '../../utils/generate-cro' /** @see {@link https://elevenlabs.io/docs/api-reference/text-to-speech/convert#request} */ export interface UnElevenLabsOptions { /** - * This parameter controls text normalization with three modes: ‘auto’, ‘on’, and ‘off’. When set to ‘auto’, + * This parameter controls text normalization with three modes: 'auto', 'on', and 'off'. When set to 'auto', * the system will automatically decide whether to apply text normalization (e.g., spelling out numbers). - * With ‘on’, text normalization will always be applied, while with ‘off’, it will be skipped. Cannot be - * turned on for ‘eleven_turbo_v2_5’ model. + * With 'on', text normalization will always be applied, while with 'off', it will be skipped. Cannot be + * turned on for 'eleven_turbo_v2_5' model. */ applyTextNormalization?: 'auto' | 'off' | 'on' /** @@ -63,7 +63,7 @@ export interface UnElevenLabsOptions { */ seed?: number /** - * Voice settings overriding stored setttings for the given voice. They are applied only on the given request. + * Voice settings overriding stored settings for the given voice. They are applied only on the given request. */ voiceSettings?: { similarityBoost?: number diff --git a/packages/providers/src/types/provider.ts b/packages/providers/src/types/provider.ts index 6c33e4c2..e459843b 100644 --- a/packages/providers/src/types/provider.ts +++ b/packages/providers/src/types/provider.ts @@ -1,10 +1,12 @@ import type { CommonRequestOptions } from '@xsai/shared' export interface ChatProvider { + // eslint-disable-next-line sonarjs/no-useless-intersection chat: (model: (string & {}) | T) => CommonRequestOptions } export interface EmbedProvider { + // eslint-disable-next-line sonarjs/no-useless-intersection embed: (model: (string & {}) | T) => CommonRequestOptions } @@ -22,10 +24,13 @@ export type ProviderResult = Omit & Partial { speech: T2 extends undefined + // eslint-disable-next-line sonarjs/no-useless-intersection ? (model: (string & {}) | T) => CommonRequestOptions + // eslint-disable-next-line sonarjs/no-useless-intersection : (model: (string & {}) | T, extraOptions?: T2) => CommonRequestOptions & T2 } export interface TranscriptionProvider { + // eslint-disable-next-line sonarjs/no-useless-intersection transcription: (model: (string & {}) | T) => CommonRequestOptions } diff --git a/packages/shared/src/utils/request-headers.ts b/packages/shared/src/utils/request-headers.ts index da91bd36..b9a8c9a8 100644 --- a/packages/shared/src/utils/request-headers.ts +++ b/packages/shared/src/utils/request-headers.ts @@ -3,6 +3,6 @@ import type { CommonRequestOptions } from '../types' import { clean } from './clean' export const requestHeaders = (headers?: CommonRequestOptions['headers'], apiKey?: CommonRequestOptions['apiKey']) => clean({ - Authorization: apiKey ? `Bearer ${apiKey}` : undefined, + Authorization: apiKey !== undefined ? `Bearer ${apiKey}` : undefined, ...headers, }) diff --git a/packages/shared/src/utils/response-json.ts b/packages/shared/src/utils/response-json.ts index 4c8f2d3f..788beef2 100644 --- a/packages/shared/src/utils/response-json.ts +++ b/packages/shared/src/utils/response-json.ts @@ -1,5 +1,6 @@ import { responseCatch } from './response-catch' export const responseJSON = async (res: Response) => + // eslint-disable-next-line @masknet/no-then responseCatch(res) .then(async res => res.json() as Promise) diff --git a/packages/stream-object/package.json b/packages/stream-object/package.json index 5e5f2513..f62df2d9 100644 --- a/packages/stream-object/package.json +++ b/packages/stream-object/package.json @@ -37,7 +37,6 @@ }, "dependencies": { "@typeschema/main": "catalog:", - "@xsai/shared": "workspace:", "@xsai/stream-text": "workspace:" }, "devDependencies": { diff --git a/packages/stream-object/src/index.ts b/packages/stream-object/src/index.ts index 1b963d4a..088bf432 100644 --- a/packages/stream-object/src/index.ts +++ b/packages/stream-object/src/index.ts @@ -1,7 +1,6 @@ import type { PartialDeep } from 'type-fest' import { type Infer, type Schema, toJSONSchema } from '@typeschema/main' -import { clean } from '@xsai/shared' import { streamText, type StreamTextOptions, type StreamTextResult } from '@xsai/stream-text' import { parse } from 'best-effort-json-parser' @@ -17,17 +16,14 @@ export interface StreamObjectResult extends StreamTextResult { /** @experimental WIP */ export const streamObject = async (options: StreamObjectOptions): Promise> => +// eslint-disable-next-line @masknet/no-then streamText({ ...options, response_format: { json_schema: { description: options.schemaDescription, name: options.schemaName ?? 'json_schema', - schema: await toJSONSchema(options.schema) - .then(json => clean({ - ...json, - $schema: undefined, - })), + schema: await toJSONSchema(options.schema), strict: true, }, type: 'json_schema', @@ -39,19 +35,18 @@ export const streamObject = async (options: StreamObjectOption const [textStream, rawPartialObjectStream] = rawTextStream.tee() let partialObjectData = '' - let partialObjectSnapshot = {} + let partialObjectSnapshot = {} as PartialDeep> const partialObjectStream = rawPartialObjectStream.pipeThrough(new TransformStream({ transform: (chunk, controller) => { partialObjectData += chunk try { - const data = parse(partialObjectData) + const data = parse(partialObjectData) as PartialDeep> if (JSON.stringify(partialObjectSnapshot) !== JSON.stringify(data)) { partialObjectSnapshot = data controller.enqueue(data) } } - // TODO: maybe handle catch {} }, })) diff --git a/packages/stream-object/test/index.test.ts b/packages/stream-object/test/index.test.ts index 82f7010e..bfc48602 100644 --- a/packages/stream-object/test/index.test.ts +++ b/packages/stream-object/test/index.test.ts @@ -4,6 +4,16 @@ import { describe, expect, it } from 'vitest' import { streamObject } from '../src' +// make TS happy +// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#browser_compatibility +declare global { + interface ReadableStream { + // eslint-disable-next-line ts/method-signature-style + [Symbol.asyncIterator](): AsyncIterableIterator + } +} + +// eslint-disable-next-line @masknet/no-top-level describe('@xsai/stream-object', () => { it('basic', async () => { const { partialObjectStream } = await streamObject({ diff --git a/packages/stream-text/src/index.ts b/packages/stream-text/src/index.ts index 15b6d393..b9f2cc89 100644 --- a/packages/stream-text/src/index.ts +++ b/packages/stream-text/src/index.ts @@ -44,11 +44,13 @@ export interface StreamTextResult { usage?: Usage } +// eslint-disable-next-line @masknet/string-no-data-url const chunkHeaderPrefix = 'data:' /** * @experimental WIP, does not support function calling (tools). */ +// eslint-disable-next-line @masknet/no-then export const streamText = async (options: StreamTextOptions): Promise => chat({ ...options, stream: true, @@ -87,16 +89,16 @@ export const streamText = async (options: StreamTextOptions): Promise { + transform: async (chunk, controller: TransformStreamDefaultController) => { const text = decoder.decode(chunk, { stream: true }) buffer += text const lines = buffer.split('\n') - buffer = lines.pop() || '' + buffer = lines.pop() ?? '' // Process complete lines for (const line of lines) { diff --git a/packages/stream-text/test/index.test.ts b/packages/stream-text/test/index.test.ts index b9cd98a1..46a7f515 100644 --- a/packages/stream-text/test/index.test.ts +++ b/packages/stream-text/test/index.test.ts @@ -2,8 +2,18 @@ import { ollama } from '@xsai/providers' import { clean } from '@xsai/shared' import { describe, expect, it } from 'vitest' -import { streamText, type StreamTextResponse } from '../src' +import { type ChunkResult, streamText } from '../src' +// make TS happy +// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#browser_compatibility +declare global { + interface ReadableStream { + // eslint-disable-next-line ts/method-signature-style + [Symbol.asyncIterator](): AsyncIterableIterator + } +} + +// eslint-disable-next-line @masknet/no-top-level describe('@xsai/stream-text', () => { it('basic', async () => { const { textStream } = await streamText({ @@ -49,7 +59,7 @@ describe('@xsai/stream-text', () => { onChunk: () => { onChunkCount++ }, }) - const chunk: StreamTextResponse[] = [] + const chunk: ChunkResult[] = [] const text: string[] = [] for await (const chunkPart of chunkStream) { @@ -71,6 +81,4 @@ describe('@xsai/stream-text', () => { expect(onChunkCount).toMatchSnapshot(chunkCount) }) - - // TODO: error handling }) diff --git a/packages/tool/src/generate-text.ts b/packages/tool/src/generate-text.ts index 17b35a3f..22979a30 100644 --- a/packages/tool/src/generate-text.ts +++ b/packages/tool/src/generate-text.ts @@ -1,6 +1,4 @@ import type { Schema } from '@typeschema/main' -// eslint-disable-next-line unused-imports/no-unused-imports -import type { GenerateTextOptions as _GTO } from '@xsai/generate-text' import type { ToolResult } from '.' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12277b15..a4199286 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -187,9 +187,6 @@ importers: '@xsai/generate-text': specifier: 'workspace:' version: link:../generate-text - '@xsai/shared': - specifier: 'workspace:' - version: link:../shared devDependencies: '@gcornut/valibot-json-schema': specifier: 'catalog:' @@ -263,9 +260,6 @@ importers: '@typeschema/main': specifier: 'catalog:' version: 0.14.1(@types/json-schema@7.0.15)(@typeschema/valibot@0.14.0(@gcornut/valibot-json-schema@0.42.0(esbuild@0.24.2)(typescript@5.6.3))(@types/json-schema@7.0.15)(valibot@0.42.1(typescript@5.6.3))) - '@xsai/shared': - specifier: 'workspace:' - version: link:../shared '@xsai/stream-text': specifier: 'workspace:' version: link:../stream-text @@ -8421,7 +8415,7 @@ snapshots: '@vue/compiler-core@3.5.13': dependencies: - '@babel/parser': 7.26.2 + '@babel/parser': 7.26.5 '@vue/shared': 3.5.13 entities: 4.5.0 estree-walker: 2.0.2 @@ -8434,7 +8428,7 @@ snapshots: '@vue/compiler-sfc@3.5.13': dependencies: - '@babel/parser': 7.26.2 + '@babel/parser': 7.26.5 '@vue/compiler-core': 3.5.13 '@vue/compiler-dom': 3.5.13 '@vue/compiler-ssr': 3.5.13