fix(decision-ollama): use Ollama's native JSON mode without thinking
CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 5s
Security Scan (SIAX Cloud) / security (push) Successful in 14s
CI (SIAX Cloud) / security (push) Successful in 17s
masterplan-lock / check (push) Successful in 17s
CI (SIAX Cloud) / security (pull_request) Successful in 20s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 21s
CI (SIAX Cloud) / integration (pull_request) Successful in 54s
CI (SIAX Cloud) / quality (push) Successful in 1m24s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m29s
CI (SIAX Cloud) / integration (push) Skipped
CI (SIAX Cloud) / sonar (push) Skipped
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (pull_request) Successful in 5s
Security Scan (SIAX Cloud) / security (push) Successful in 14s
CI (SIAX Cloud) / security (push) Successful in 17s
masterplan-lock / check (push) Successful in 17s
CI (SIAX Cloud) / security (pull_request) Successful in 20s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 21s
CI (SIAX Cloud) / integration (pull_request) Successful in 54s
CI (SIAX Cloud) / quality (push) Successful in 1m24s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m29s
The 9B model spent its whole 4000-token budget on reasoning and came back model_incomplete. Call /api/chat with format=json, think=false and num_predict, and read prompt_eval_count/eval_count/done_reason. Structured extraction no longer needs chain-of-thought.
This commit is contained in:
@@ -100,7 +100,7 @@ export class DecisionOllamaGatewayClient {
|
||||
|
||||
let response: Response;
|
||||
try {
|
||||
response = await this.fetchImpl(`${this.baseUrl}/v1/chat/completions`, {
|
||||
response = await this.fetchImpl(`${this.baseUrl}/api/chat`, {
|
||||
method: 'POST',
|
||||
redirect: 'error',
|
||||
signal: AbortSignal.timeout(this.options.timeoutMs ?? 120_000),
|
||||
@@ -113,9 +113,20 @@ export class DecisionOllamaGatewayClient {
|
||||
body: JSON.stringify({
|
||||
model: this.options.model,
|
||||
messages,
|
||||
max_tokens: admission.maxOutputTokens,
|
||||
temperature: 0,
|
||||
stream: false,
|
||||
// Structured extraction: JSON mode, no chain-of-thought output, and a
|
||||
// hard output budget. A thinking model otherwise spends the whole
|
||||
// budget on reasoning and returns model_incomplete.
|
||||
format: 'json',
|
||||
think: false,
|
||||
options: {
|
||||
temperature: 0,
|
||||
num_predict: admission.maxOutputTokens,
|
||||
num_ctx: Math.max(
|
||||
4096,
|
||||
Math.min(131072, admission.maxInputTokens + admission.maxOutputTokens + 1024),
|
||||
),
|
||||
},
|
||||
}),
|
||||
});
|
||||
} catch {
|
||||
@@ -131,13 +142,15 @@ export class DecisionOllamaGatewayClient {
|
||||
}
|
||||
const result = decoded as {
|
||||
model?: unknown;
|
||||
choices?: Array<{ message?: { content?: unknown }; finish_reason?: unknown }>;
|
||||
usage?: { prompt_tokens?: unknown; completion_tokens?: unknown; total_tokens?: unknown };
|
||||
message?: { content?: unknown };
|
||||
done?: unknown;
|
||||
done_reason?: unknown;
|
||||
prompt_eval_count?: unknown;
|
||||
eval_count?: unknown;
|
||||
} | null;
|
||||
const content = result?.choices?.[0]?.message?.content;
|
||||
const promptTokens = result?.usage?.prompt_tokens;
|
||||
const completionTokens = result?.usage?.completion_tokens;
|
||||
const totalTokens = result?.usage?.total_tokens;
|
||||
const content = result?.message?.content;
|
||||
const promptTokens = result?.prompt_eval_count;
|
||||
const completionTokens = result?.eval_count;
|
||||
if (
|
||||
!result ||
|
||||
typeof content !== 'string' ||
|
||||
@@ -145,16 +158,15 @@ export class DecisionOllamaGatewayClient {
|
||||
!result.model.trim() ||
|
||||
!Number.isSafeInteger(promptTokens) ||
|
||||
!Number.isSafeInteger(completionTokens) ||
|
||||
!Number.isSafeInteger(totalTokens) ||
|
||||
(promptTokens as number) < 0 ||
|
||||
(promptTokens as number) > admission.maxInputTokens ||
|
||||
(completionTokens as number) > admission.maxOutputTokens ||
|
||||
(promptTokens as number) + (completionTokens as number) !== totalTokens
|
||||
(completionTokens as number) > admission.maxOutputTokens
|
||||
) {
|
||||
throw new Inf0OutcomeUnknownError();
|
||||
}
|
||||
const totalTokens = (promptTokens as number) + (completionTokens as number);
|
||||
|
||||
const finish = result.choices?.[0]?.finish_reason;
|
||||
const finish = result.done_reason;
|
||||
return {
|
||||
status:
|
||||
finish === 'stop' && (completionTokens as number) > 0 && content.trim()
|
||||
|
||||
Reference in New Issue
Block a user