diff --git a/packages/core/src/ai-model/models/deepseek/adapter.ts b/packages/core/src/ai-model/models/deepseek/adapter.ts index 371ac39837..5ea83a383f 100644 --- a/packages/core/src/ai-model/models/deepseek/adapter.ts +++ b/packages/core/src/ai-model/models/deepseek/adapter.ts @@ -30,14 +30,18 @@ function parseDeepSeekCoordinateValues( expectedLength: number, label: string, ): number[] { - const coordinateTexts = String(input).match(/\d+/g); - if (coordinateTexts?.length !== expectedLength) { + const coordinateTexts = String(input).match(/[+-]?\d+/g); + const coordinates = coordinateTexts?.map(Number); + if ( + coordinates?.length !== expectedLength || + !coordinates.every((coordinate) => coordinate >= 0) + ) { throw new Error( `DeepSeek ${label} locate result must contain exactly ${expectedLength} positive integers, got ${coordinateTexts?.length ?? 0}`, ); } - return coordinateTexts.map(Number); + return coordinates; } function parseDeepSeekPointLocateValue(input: unknown): LocateResultValue { diff --git a/packages/core/tests/unit-test/model-adapter/deepseek.test.ts b/packages/core/tests/unit-test/model-adapter/deepseek.test.ts index 235565a3ee..42183454c1 100644 --- a/packages/core/tests/unit-test/model-adapter/deepseek.test.ts +++ b/packages/core/tests/unit-test/model-adapter/deepseek.test.ts @@ -209,6 +209,8 @@ describe('deepseek model adapter', () => { it.each([ '<||point||>[[928.5,780]]<||/point||>', '<||point||>no coordinates<||/point||>', + '<||point||>[-10,500]<||/point||>', + '<||point||>[500,-10]<||/point||>', ])('rejects output without exactly two integers: %s', (content) => { const locateAdapter = getStandardLocateAdapter().element; const rawResult = locateAdapter.protocol.parseRawResponse( @@ -226,6 +228,28 @@ describe('deepseek model adapter', () => { ).toThrow('must contain exactly 2 positive integers'); }); + it.each([ + '<||ref||>target<||/ref||><||box||>[[-1,100,200,300]]<||/box||>', + '<||ref||>target<||/ref||><||box||>[[100,-1,200,300]]<||/box||>', + '<||ref||>target<||/ref||><||box||>[[100,200,-1,300]]<||/box||>', + '<||ref||>target<||/ref||><||box||>[[100,200,300,-1]]<||/box||>', + ])('rejects negative bbox coordinates: %s', (content) => { + const locateAdapter = getStandardLocateAdapter().searchArea!; + const rawResult = locateAdapter.protocol.parseRawResponse( + content, + locateAdapter.resultCodec.promptSpec, + ); + if (rawResult.kind !== 'located') { + throw new Error('DeepSeek response should contain a raw location'); + } + + expect(() => + locateAdapter.resultCodec.toPixelBbox(rawResult.target, { + preparedSize: { width: 1000, height: 1000 }, + }), + ).toThrow('must contain exactly 4 positive integers'); + }); + it.each([ '<||point||>[928,780]<||/point||>', '<||point||>[[928 780]]<||/point||>',