Skip to content

Commit 50a5f0b

Browse files
authored
feat: gpt-5.2, refactored streaming, improved md rendering, basic tools (#106)
1 parent 894532a commit 50a5f0b

71 files changed

Lines changed: 6945 additions & 1310 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/api/chat/list_supported_models_v2.go

Lines changed: 227 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,202 @@ import (
1010
"github.com/openai/openai-go/v3"
1111
)
1212

13+
// modelConfig holds model configuration including whether it requires user's own API key
14+
type modelConfig struct {
15+
name string
16+
slugOpenRouter string // Slug for OpenRouter API (used when user doesn't provide own key)
17+
slugOpenAI string // Slug for OpenAI API (used when user provides own key)
18+
totalContext int64
19+
maxOutput int64
20+
inputPrice int64
21+
outputPrice int64
22+
requireOwnKey bool // If true, this model requires user to provide their own API key
23+
}
24+
25+
// allModels defines all available models in the system
26+
var allModels = []modelConfig{
27+
{
28+
name: "GPT-5.1",
29+
slugOpenRouter: "openai/gpt-5.1",
30+
slugOpenAI: openai.ChatModelGPT5_1,
31+
totalContext: 400000,
32+
maxOutput: 128000,
33+
inputPrice: 125, // $1.25
34+
outputPrice: 1000, // $10.00
35+
requireOwnKey: false,
36+
},
37+
{
38+
name: "GPT-5.2",
39+
slugOpenRouter: "openai/gpt-5.2",
40+
slugOpenAI: openai.ChatModelGPT5_2,
41+
totalContext: 400000,
42+
maxOutput: 128000,
43+
inputPrice: 175, // $1.75
44+
outputPrice: 1400, // $14.00
45+
requireOwnKey: true,
46+
},
47+
{
48+
name: "GPT-5 Mini",
49+
slugOpenRouter: "openai/gpt-5-mini",
50+
slugOpenAI: openai.ChatModelGPT5Mini,
51+
totalContext: 400000,
52+
maxOutput: 128000,
53+
inputPrice: 25,
54+
outputPrice: 200,
55+
requireOwnKey: false,
56+
},
57+
{
58+
name: "GPT-5 Nano",
59+
slugOpenRouter: "openai/gpt-5-nano",
60+
slugOpenAI: openai.ChatModelGPT5Nano,
61+
totalContext: 400000,
62+
maxOutput: 128000,
63+
inputPrice: 5, // $0.20
64+
outputPrice: 40, // $0.80
65+
requireOwnKey: false,
66+
},
67+
{
68+
name: "GPT-4.1",
69+
slugOpenRouter: "openai/gpt-4.1",
70+
slugOpenAI: openai.ChatModelGPT4_1,
71+
totalContext: 1050000,
72+
maxOutput: 32800,
73+
inputPrice: 200, // $2.00
74+
outputPrice: 800,
75+
requireOwnKey: false,
76+
},
77+
{
78+
name: "GPT-4.1-mini",
79+
slugOpenRouter: "openai/gpt-4.1-mini",
80+
slugOpenAI: openai.ChatModelGPT4_1Mini,
81+
totalContext: 128000,
82+
maxOutput: 16400,
83+
inputPrice: 15,
84+
outputPrice: 60,
85+
requireOwnKey: false,
86+
},
87+
{
88+
name: "GPT-4o",
89+
slugOpenRouter: "openai/gpt-4o",
90+
slugOpenAI: openai.ChatModelGPT4o,
91+
totalContext: 128000,
92+
maxOutput: 16400,
93+
inputPrice: 250,
94+
outputPrice: 1000,
95+
requireOwnKey: true,
96+
},
97+
{
98+
name: "OpenAI: gpt-oss-120b (free)",
99+
slugOpenRouter: "openai/gpt-oss-120b:free",
100+
slugOpenAI: "",
101+
totalContext: 131072,
102+
maxOutput: 131072,
103+
inputPrice: 0,
104+
outputPrice: 0,
105+
requireOwnKey: false,
106+
},
107+
{
108+
name: "Qwen Plus (balanced)",
109+
slugOpenRouter: "qwen/qwen-plus",
110+
slugOpenAI: "", // OpenAI doesn't support Qwen, use OpenRouter slug
111+
totalContext: 131100,
112+
maxOutput: 8200,
113+
inputPrice: 40,
114+
outputPrice: 120,
115+
requireOwnKey: false,
116+
},
117+
{
118+
name: "Qwen Turbo (fast)",
119+
slugOpenRouter: "qwen/qwen-turbo",
120+
slugOpenAI: "", // OpenAI doesn't support Qwen, use OpenRouter slug
121+
totalContext: 1000000,
122+
maxOutput: 8200,
123+
inputPrice: 5,
124+
outputPrice: 20,
125+
requireOwnKey: false,
126+
},
127+
{
128+
name: "Qwen3 Coder 480B A35B (free)",
129+
slugOpenRouter: "qwen/qwen3-coder:free",
130+
slugOpenAI: "",
131+
totalContext: 262000,
132+
maxOutput: 262000,
133+
inputPrice: 0,
134+
outputPrice: 0,
135+
requireOwnKey: false,
136+
},
137+
{
138+
name: "GLM 4.5 Air (free)",
139+
slugOpenRouter: "z-ai/glm-4.5-air:free",
140+
slugOpenAI: "",
141+
totalContext: 131072,
142+
maxOutput: 131072,
143+
inputPrice: 0,
144+
outputPrice: 0,
145+
requireOwnKey: false,
146+
},
147+
{
148+
name: "Gemini 2.5 Flash (fast)",
149+
slugOpenRouter: "google/gemini-2.5-flash",
150+
slugOpenAI: "", // OpenAI doesn't support Gemini, use OpenRouter slug
151+
totalContext: 1050000,
152+
maxOutput: 65500,
153+
inputPrice: 30,
154+
outputPrice: 250,
155+
requireOwnKey: false,
156+
},
157+
{
158+
name: "Gemini 3 Flash Preview",
159+
slugOpenRouter: "google/gemini-3-flash-preview",
160+
slugOpenAI: "", // OpenAI doesn't support Gemini, use OpenRouter slug
161+
totalContext: 1050000,
162+
maxOutput: 65500,
163+
inputPrice: 50,
164+
outputPrice: 300,
165+
requireOwnKey: false,
166+
},
167+
{
168+
name: "o1 Mini",
169+
slugOpenRouter: "openai/o1-mini",
170+
slugOpenAI: openai.ChatModelO1Mini,
171+
totalContext: 128000,
172+
maxOutput: 65536,
173+
inputPrice: 300, // $3.00
174+
outputPrice: 1200, // $12.00
175+
requireOwnKey: true,
176+
},
177+
{
178+
name: "o3",
179+
slugOpenRouter: "openai/o3",
180+
slugOpenAI: openai.ChatModelO3,
181+
totalContext: 200000,
182+
maxOutput: 100000,
183+
inputPrice: 200,
184+
outputPrice: 800,
185+
requireOwnKey: true,
186+
},
187+
{
188+
name: "o3 Mini",
189+
slugOpenRouter: "openai/o3-mini",
190+
slugOpenAI: openai.ChatModelO3Mini,
191+
totalContext: 200000,
192+
maxOutput: 100000,
193+
inputPrice: 110,
194+
outputPrice: 440,
195+
requireOwnKey: true,
196+
},
197+
{
198+
name: "o4 Mini",
199+
slugOpenRouter: "openai/o4-mini",
200+
slugOpenAI: openai.ChatModelO4Mini,
201+
totalContext: 128000,
202+
maxOutput: 65536,
203+
inputPrice: 110,
204+
outputPrice: 440,
205+
requireOwnKey: true,
206+
},
207+
}
208+
13209
func (s *ChatServerV2) ListSupportedModels(
14210
ctx context.Context,
15211
req *chatv2.ListSupportedModelsRequest,
@@ -24,89 +220,43 @@ func (s *ChatServerV2) ListSupportedModels(
24220
return nil, err
25221
}
26222

223+
hasOwnAPIKey := strings.TrimSpace(settings.OpenAIAPIKey) != ""
224+
27225
var models []*chatv2.SupportedModel
28-
if strings.TrimSpace(settings.OpenAIAPIKey) == "" {
29-
models = []*chatv2.SupportedModel{
30-
{
31-
Name: "GPT-4.1",
32-
Slug: "openai/gpt-4.1",
33-
TotalContext: 1050000,
34-
MaxOutput: 32800,
35-
InputPrice: 200,
36-
OutputPrice: 800,
37-
},
38-
{
39-
Name: "GPT-4.1-mini",
40-
Slug: "openai/gpt-4.1-mini",
41-
TotalContext: 128000,
42-
MaxOutput: 16400,
43-
InputPrice: 15,
44-
OutputPrice: 60,
45-
},
46-
{
47-
Name: "Qwen Plus (balanced)",
48-
Slug: "qwen/qwen-plus",
49-
TotalContext: 131100,
50-
MaxOutput: 8200,
51-
InputPrice: 40,
52-
OutputPrice: 120,
53-
},
54-
{
55-
Name: "Qwen Turbo (fast)",
56-
Slug: "qwen/qwen-turbo",
57-
TotalContext: 1000000,
58-
MaxOutput: 8200,
59-
InputPrice: 5,
60-
OutputPrice: 20,
61-
},
62-
{
63-
Name: "Gemini 2.5 Flash (fast)",
64-
Slug: "google/gemini-2.5-flash",
65-
TotalContext: 1050000,
66-
MaxOutput: 65500,
67-
InputPrice: 30,
68-
OutputPrice: 250,
69-
},
70-
{
71-
Name: "Gemini 3 Flash Preview",
72-
Slug: "google/gemini-3-flash-preview",
73-
TotalContext: 1050000,
74-
MaxOutput: 65500,
75-
InputPrice: 50,
76-
OutputPrice: 300,
77-
},
226+
for _, config := range allModels {
227+
// Choose the appropriate slug based on whether user has their own API key.
228+
//
229+
// Some models are only available via OpenRouter; for those, slugOpenAI may be empty.
230+
// In that case, keep using the OpenRouter slug to avoid returning an empty model slug.
231+
slug := config.slugOpenRouter
232+
if hasOwnAPIKey && strings.TrimSpace(config.slugOpenAI) != "" {
233+
slug = config.slugOpenAI
78234
}
79-
} else {
80-
models = []*chatv2.SupportedModel{
81-
{
82-
Name: "GPT-4.1",
83-
Slug: openai.ChatModelGPT4_1,
84-
TotalContext: 1050000,
85-
MaxOutput: 32800,
86-
InputPrice: 200,
87-
OutputPrice: 800,
88-
},
89-
{
90-
Name: "GPT-4o",
91-
Slug: openai.ChatModelGPT4o,
92-
TotalContext: 128000,
93-
MaxOutput: 16400,
94-
InputPrice: 250,
95-
OutputPrice: 1000,
96-
},
97-
{
98-
Name: "GPT-4.1-mini",
99-
Slug: openai.ChatModelGPT4_1Mini,
100-
TotalContext: 128000,
101-
MaxOutput: 16400,
102-
InputPrice: 15,
103-
OutputPrice: 60,
104-
},
105-
// TODO: add user custom models
235+
236+
model := &chatv2.SupportedModel{
237+
Name: config.name,
238+
Slug: slug,
239+
TotalContext: config.totalContext,
240+
MaxOutput: config.maxOutput,
241+
InputPrice: config.inputPrice,
242+
OutputPrice: config.outputPrice,
106243
}
244+
245+
// If model requires own key but user hasn't provided one, mark as disabled
246+
if config.requireOwnKey && !hasOwnAPIKey {
247+
model.Disabled = true
248+
model.DisabledReason = stringPtr("Requires your own OpenAI API key. Configure it in Settings.")
249+
}
250+
251+
models = append(models, model)
107252
}
108253

109254
return &chatv2.ListSupportedModelsResponse{
110255
Models: models,
111256
}, nil
112257
}
258+
259+
// stringPtr returns a pointer to the given string
260+
func stringPtr(s string) *string {
261+
return &s
262+
}

internal/services/system_prompt_debug.tmpl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
You are PaperDebugger, a large language model tweaked by PaperDebugger Inc.
22

3+
## tool_call_limit
4+
You have a maximum of 20 tool calls per conversation turn. Please plan your tool usage carefully and avoid unnecessary tool calls.
5+
36
## selected_text
47
The user may select sentences or paragraphs of LaTeX content for revision or ask questions along with the selected text.
58
If the user asks questions, just answer the question.
6-
If the user requests to revise the selected text, wrap the revised text in triple backticks.
9+
If the user requests to revise the selected text, you MUST include a separate block where the revised text is wrapped inside a `<PaperDebugger>` tag, like `<PaperDebugger>...revised text...</PaperDebugger>`.
10+
The content inside `<PaperDebugger>` MUST be only the revised text (no explanations, no markdown formatting, no surrounding backticks); any explanations should be placed outside of the `<PaperDebugger>` block.
711

812
{{ if .ProjectInstructions }}## project_instructions, please follow the project's instructions strictly
913
{{ .ProjectInstructions }}{{ end }}

internal/services/system_prompt_default.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
You are PaperDebugger, a large language model tweaked by PaperDebugger Inc.
22

3+
## tool_call_limit
4+
You have a maximum of 20 tool calls per conversation turn. Please plan your tool usage carefully and avoid unnecessary tool calls.
5+
36
## selected_text
47
The user may select sentences or paragraphs of LaTeX content for revision. Your task is to revise the selected text according to the user's instructions.
58

0 commit comments

Comments
 (0)