mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-30 11:22:34 +00:00
feat(ai): add Alibaba DashScope recipe (#59 split, part 1/2)
12th recipe. text-embedding-v3 (current) + text-embedding-v2; 1024 default dims with Matryoshka options [64, 128, 256, 512, 768, 1024]. OpenAI-compatible at dashscope-intl.aliyuncs.com. China-region users override via cfg.base_urls['dashscope']; v0.32 ships with the international default. Conservative max_batch_tokens=8192 + chars_per_token=2 declared because Alibaba doesn't publish a hard batch limit and text-embedding-v3 mixes English + CJK heavily (CJK density closer to Voyage than OpenAI tiktoken). Tests: bun test test/ai/ — 106/106 (5 new + 101 prior). Plan: ~/.claude/plans/ok-lets-turn-this-enumerated-sonnet.md (commit 6 of 11). Reworked from #59 (DashScope+Zhipu split into 2 commits per the plan; Zhipu lands next). Co-Authored-By: Magicray1217 <267836857+Magicray1217@users.noreply.github.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Magicray1217
Claude Opus 4.7
parent
1abbe8d5a0
commit
ce05aef3de
@@ -0,0 +1,42 @@
|
||||
import type { Recipe } from '../types.ts';
|
||||
|
||||
/**
|
||||
* Alibaba DashScope (灵积). OpenAI-compatible /embeddings endpoint at
|
||||
* dashscope-intl.aliyuncs.com. Hosts text-embedding-v2 (older) and
|
||||
* text-embedding-v3 (current; Matryoshka-aware up to 1024 dims).
|
||||
*
|
||||
* Reference: https://help.aliyun.com/zh/model-studio/getting-started/
|
||||
*
|
||||
* Note: the international endpoint requires a region-aware DASHSCOPE_API_KEY.
|
||||
* China-region users typically point at https://dashscope.aliyuncs.com/...
|
||||
* via cfg.base_urls['dashscope']. v0.32 ships with the international
|
||||
* default; users override per the recipe convention.
|
||||
*/
|
||||
export const dashscope: Recipe = {
|
||||
id: 'dashscope',
|
||||
name: 'Alibaba DashScope (灵积)',
|
||||
tier: 'openai-compat',
|
||||
implementation: 'openai-compatible',
|
||||
base_url_default: 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
|
||||
auth_env: {
|
||||
required: ['DASHSCOPE_API_KEY'],
|
||||
setup_url: 'https://help.aliyun.com/zh/model-studio/getting-started/',
|
||||
},
|
||||
touchpoints: {
|
||||
embedding: {
|
||||
models: ['text-embedding-v3', 'text-embedding-v2'],
|
||||
default_dims: 1024,
|
||||
dims_options: [64, 128, 256, 512, 768, 1024],
|
||||
// Alibaba doesn't publish a hard batch-token cap for the OpenAI-compat
|
||||
// path. Conservative declaration so the gateway pre-splits before
|
||||
// hitting whatever undocumented server-side limit exists.
|
||||
max_batch_tokens: 8192,
|
||||
// text-embedding-v3 mixes English + CJK heavily; the tokenizer is
|
||||
// closer to Voyage density than OpenAI tiktoken for CJK-dominant
|
||||
// content. Conservative chars_per_token=2 leaves headroom.
|
||||
chars_per_token: 2,
|
||||
},
|
||||
},
|
||||
setup_hint:
|
||||
'Get an API key at https://help.aliyun.com/zh/model-studio/getting-started/, then `export DASHSCOPE_API_KEY=...`',
|
||||
};
|
||||
@@ -17,6 +17,7 @@ import { groq } from './groq.ts';
|
||||
import { together } from './together.ts';
|
||||
import { llamaServer } from './llama-server.ts';
|
||||
import { minimax } from './minimax.ts';
|
||||
import { dashscope } from './dashscope.ts';
|
||||
|
||||
const ALL: Recipe[] = [
|
||||
openai,
|
||||
@@ -30,6 +31,7 @@ const ALL: Recipe[] = [
|
||||
together,
|
||||
llamaServer,
|
||||
minimax,
|
||||
dashscope,
|
||||
];
|
||||
|
||||
/** Map from `provider:id` key to recipe. */
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* DashScope (Alibaba) recipe smoke (Commit 6 of the v0.32 wave).
|
||||
*/
|
||||
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { getRecipe } from '../../src/core/ai/recipes/index.ts';
|
||||
import { defaultResolveAuth } from '../../src/core/ai/gateway.ts';
|
||||
import { AIConfigError } from '../../src/core/ai/errors.ts';
|
||||
|
||||
describe('recipe: dashscope', () => {
|
||||
test('registered with expected shape', () => {
|
||||
const r = getRecipe('dashscope');
|
||||
expect(r).toBeDefined();
|
||||
expect(r!.id).toBe('dashscope');
|
||||
expect(r!.tier).toBe('openai-compat');
|
||||
expect(r!.implementation).toBe('openai-compatible');
|
||||
expect(r!.base_url_default).toBe(
|
||||
'https://dashscope-intl.aliyuncs.com/compatible-mode/v1',
|
||||
);
|
||||
expect(r!.auth_env?.required).toEqual(['DASHSCOPE_API_KEY']);
|
||||
});
|
||||
|
||||
test('embedding touchpoint declares text-embedding-v3 first + 1024 dims', () => {
|
||||
const r = getRecipe('dashscope')!;
|
||||
expect(r.touchpoints.embedding).toBeDefined();
|
||||
expect(r.touchpoints.embedding!.models[0]).toBe('text-embedding-v3');
|
||||
expect(r.touchpoints.embedding!.models).toContain('text-embedding-v2');
|
||||
expect(r.touchpoints.embedding!.default_dims).toBe(1024);
|
||||
expect(r.touchpoints.embedding!.dims_options).toEqual([64, 128, 256, 512, 768, 1024]);
|
||||
// Matryoshka: every dims option ≤ 2000 (HNSW-compatible).
|
||||
for (const d of r.touchpoints.embedding!.dims_options ?? []) {
|
||||
expect(d).toBeLessThanOrEqual(2000);
|
||||
}
|
||||
});
|
||||
|
||||
test('default auth: DASHSCOPE_API_KEY set → "Bearer <key>"', () => {
|
||||
const r = getRecipe('dashscope')!;
|
||||
const auth = defaultResolveAuth(
|
||||
r,
|
||||
{ DASHSCOPE_API_KEY: 'sk-dashscope-fake' },
|
||||
'embedding',
|
||||
);
|
||||
expect(auth.headerName).toBe('Authorization');
|
||||
expect(auth.token).toBe('Bearer sk-dashscope-fake');
|
||||
});
|
||||
|
||||
test('default auth: missing DASHSCOPE_API_KEY → AIConfigError', () => {
|
||||
const r = getRecipe('dashscope')!;
|
||||
expect(() => defaultResolveAuth(r, {}, 'embedding')).toThrow(AIConfigError);
|
||||
});
|
||||
|
||||
test('declares chars_per_token + max_batch_tokens for safer batching', () => {
|
||||
const r = getRecipe('dashscope')!;
|
||||
expect(r.touchpoints.embedding!.max_batch_tokens).toBeGreaterThan(0);
|
||||
expect(r.touchpoints.embedding!.chars_per_token).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user