fix(ask): Extract reasoning tokens for openai compatible models (#582)

This commit is contained in:
Brendan Kellam 2025-10-29 17:13:31 -07:00 committed by GitHub
parent 727a6da105
commit d09d65dce7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 113 additions and 2 deletions

View file

@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- [ask sb] Fixed issue where reasoning tokens would appear in `text` content for openai compatible models. [#582](https://github.com/sourcebot-dev/sourcebot/pull/582)
## [4.8.1] - 2025-10-29
### Fixed

View file

@ -292,6 +292,7 @@ The OpenAI compatible provider allows you to use any model that is compatible wi
<Accordion title="Troubleshooting">
- When using [llama.cpp](https://github.com/ggml-org/llama.cpp), if you hit "Failed after 3 attempts. Last error: tools param requires --jinja flag", add the `--jinja` flag to your `llama-server` command.
- If you're seeing the LLM outputing reasoning tokens wrapped in XML tags (e.g., `<reasoning>`, `<thinking>`, etc.), you can configure the `reasoningTag` parameter to the name of the tag (without angle brackets). This parameter defaults to `think`.
</Accordion>
### OpenRouter

View file

@ -2633,6 +2633,16 @@
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [
@ -4052,6 +4062,16 @@
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [

View file

@ -1202,6 +1202,16 @@
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [
@ -2621,6 +2631,16 @@
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [

View file

@ -2632,6 +2632,16 @@ const schema = {
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [
@ -4051,6 +4061,16 @@ const schema = {
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [

View file

@ -974,6 +974,10 @@ export interface OpenAICompatibleLanguageModel {
baseUrl: string;
headers?: LanguageModelHeaders;
queryParams?: LanguageModelQueryParams;
/**
* The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.
*/
reasoningTag?: string;
}
/**
* Optional query parameters to include in the request url.

View file

@ -1201,6 +1201,16 @@ const schema = {
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [
@ -2620,6 +2630,16 @@ const schema = {
}
},
"additionalProperties": false
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [

View file

@ -453,6 +453,10 @@ export interface OpenAICompatibleLanguageModel {
baseUrl: string;
headers?: LanguageModelHeaders;
queryParams?: LanguageModelQueryParams;
/**
* The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.
*/
reasoningTag?: string;
}
/**
* Optional query parameters to include in the request url.

View file

@ -25,7 +25,7 @@ import { ChatVisibility, OrgRole, Prisma, PrismaClient } from "@sourcebot/db";
import { LanguageModel } from "@sourcebot/schemas/v3/languageModel.type";
import { Token } from "@sourcebot/schemas/v3/shared.type";
import { loadConfig } from "@sourcebot/shared";
import { generateText, JSONValue } from "ai";
import { generateText, JSONValue, extractReasoningMiddleware, wrapLanguageModel } from "ai";
import fs from 'fs';
import { StatusCodes } from "http-status-codes";
import path from 'path';
@ -568,8 +568,17 @@ export const _getAISDKLanguageModelAndOptions = async (config: LanguageModel, or
: undefined,
});
return {
const model = wrapLanguageModel({
model: openai.chatModel(modelId),
middleware: [
extractReasoningMiddleware({
tagName: config.reasoningTag ?? 'think',
}),
]
});
return {
model,
}
}
case 'openrouter': {

View file

@ -425,6 +425,16 @@
},
"queryParams": {
"$ref": "./shared.json#/definitions/LanguageModelQueryParams"
},
"reasoningTag": {
"type": "string",
"description": "The name of the XML tag to extract reasoning from (without angle brackets). Defaults to `think`.",
"default": "think",
"examples": [
"think",
"thinking",
"reasoning"
]
}
},
"required": [