@subrouter/opencode 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +164 -42
- package/dist/opencode-e2e.test.js +354 -15
- package/dist/plugin.test.js +436 -13
- package/dist/provider.d.ts +38 -11
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +42 -7
- package/package.json +2 -2
- package/src/index.ts +195 -54
- package/src/opencode-e2e.test.ts +397 -16
- package/src/plugin.test.ts +481 -21
- package/src/provider.ts +81 -9
package/src/provider.ts
CHANGED
|
@@ -1,21 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider entry loaded by opencode via `provider.subrouter.npm` (file:// URL).
|
|
3
3
|
* OpenCode imports this module, calls the first export starting with `create`,
|
|
4
|
-
* then `sdk.languageModel(modelId)
|
|
5
|
-
*
|
|
4
|
+
* then `sdk.languageModel(modelId)`. For GPT presets that id is a spoofed
|
|
5
|
+
* `gpt-*` api id so OpenCode prefers apply_patch; createSubrouter maps it
|
|
6
|
+
* back to the preset. Also rewrites OpenCode's powered-by identity to the
|
|
7
|
+
* live routed model and appends the GPT apply_patch constraint when needed.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import {
|
|
11
|
+
OPENCODE_AGENT_HEADER,
|
|
12
|
+
OPENCODE_VARIANT_HEADER,
|
|
9
13
|
OPENAI_WEBSOCKET_SESSION_HEADER,
|
|
10
14
|
OPENAI_WEBSOCKET_TITLE_HEADER,
|
|
11
15
|
PROVIDER_ID,
|
|
12
|
-
|
|
16
|
+
resolveLiveModel,
|
|
17
|
+
ROUTE_AFFINITY_HEADER,
|
|
13
18
|
} from '@subrouter/cli'
|
|
14
19
|
|
|
15
20
|
export { createSubrouter } from '@subrouter/cli'
|
|
16
21
|
|
|
17
22
|
const POWERED_BY_MODEL = /You are powered by the model named [^\n]+/
|
|
18
23
|
|
|
24
|
+
// OpenCode v1 registry.ts and v2 patch.ts: GPT (not gpt-4 / gpt-oss) uses apply_patch.
|
|
25
|
+
export function shouldUseApplyPatch(modelId: string) {
|
|
26
|
+
return modelId.includes('gpt-') && !modelId.includes('oss') && !modelId.includes('gpt-4')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function applyPatchApiId({
|
|
30
|
+
preset,
|
|
31
|
+
modelId,
|
|
32
|
+
taken,
|
|
33
|
+
}: {
|
|
34
|
+
preset: string
|
|
35
|
+
modelId: string
|
|
36
|
+
taken: Set<string>
|
|
37
|
+
}) {
|
|
38
|
+
if (!taken.has(modelId)) return modelId
|
|
39
|
+
return `${modelId}:${preset}`
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const APPLY_PATCH_GPT =
|
|
43
|
+
'Always use apply_patch for manual code edits. Do not use cat or any other commands when creating or editing files. Formatting commands or bulk edits don\'t need to be done with apply_patch.'
|
|
44
|
+
|
|
45
|
+
const APPLY_PATCH_CODEX =
|
|
46
|
+
'Try to use apply_patch for single file edits, but it is fine to explore other options to make the edit if it does not work well. Do not use apply_patch for changes that are auto-generated (i.e. generating package.json or running a lint or format command like gofmt) or when scripting is more efficient (such as search and replacing a string across a codebase).'
|
|
47
|
+
|
|
48
|
+
export function applyPatchConstraint(modelId: string) {
|
|
49
|
+
if (modelId.includes('codex')) return APPLY_PATCH_CODEX
|
|
50
|
+
return APPLY_PATCH_GPT
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function appendApplyPatchConstraint({
|
|
54
|
+
system,
|
|
55
|
+
modelId,
|
|
56
|
+
}: {
|
|
57
|
+
system: string[]
|
|
58
|
+
modelId: string
|
|
59
|
+
}) {
|
|
60
|
+
if (!shouldUseApplyPatch(modelId)) return
|
|
61
|
+
const constraint = applyPatchConstraint(modelId)
|
|
62
|
+
if (system.some((line) => line.includes('use apply_patch'))) return
|
|
63
|
+
system.push(constraint)
|
|
64
|
+
}
|
|
65
|
+
|
|
19
66
|
export function rewritePoweredByModelLine({
|
|
20
67
|
system,
|
|
21
68
|
candidate,
|
|
@@ -32,23 +79,48 @@ export function rewritePoweredByModelLine({
|
|
|
32
79
|
export async function revealRoutedModel({
|
|
33
80
|
providerID,
|
|
34
81
|
preset,
|
|
82
|
+
sessionID,
|
|
35
83
|
system,
|
|
36
84
|
}: {
|
|
37
85
|
providerID: string
|
|
38
86
|
preset: string
|
|
87
|
+
sessionID?: string
|
|
39
88
|
system: string[]
|
|
40
89
|
}) {
|
|
41
90
|
if (providerID !== PROVIDER_ID) return
|
|
42
|
-
const candidate = await
|
|
91
|
+
const candidate = await resolveLiveModel({ preset, sessionID })
|
|
43
92
|
if (!candidate) return
|
|
44
93
|
rewritePoweredByModelLine({ system, candidate })
|
|
94
|
+
appendApplyPatchConstraint({ system, modelId: candidate.modelId })
|
|
45
95
|
}
|
|
46
96
|
|
|
47
|
-
export function addSubrouterHeaders(
|
|
48
|
-
input
|
|
49
|
-
output
|
|
50
|
-
|
|
51
|
-
|
|
97
|
+
export function addSubrouterHeaders({
|
|
98
|
+
input,
|
|
99
|
+
output,
|
|
100
|
+
affinityKey = input.message.id,
|
|
101
|
+
}: {
|
|
102
|
+
input: {
|
|
103
|
+
sessionID: string
|
|
104
|
+
agent: string
|
|
105
|
+
model: { providerID: string }
|
|
106
|
+
message: {
|
|
107
|
+
id: string
|
|
108
|
+
agent: string
|
|
109
|
+
model: { providerID: string; modelID: string; variant?: string }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
output: { headers: Record<string, string> }
|
|
113
|
+
affinityKey?: string
|
|
114
|
+
}) {
|
|
115
|
+
if (input.model.providerID !== PROVIDER_ID) return null
|
|
52
116
|
output.headers[OPENAI_WEBSOCKET_SESSION_HEADER] = input.sessionID
|
|
117
|
+
if (input.agent === input.message.agent) {
|
|
118
|
+
output.headers[ROUTE_AFFINITY_HEADER] = affinityKey
|
|
119
|
+
output.headers[OPENCODE_AGENT_HEADER] = input.message.agent
|
|
120
|
+
if (input.message.model.variant) {
|
|
121
|
+
output.headers[OPENCODE_VARIANT_HEADER] = input.message.model.variant
|
|
122
|
+
}
|
|
123
|
+
}
|
|
53
124
|
if (input.agent === 'title') output.headers[OPENAI_WEBSOCKET_TITLE_HEADER] = 'true'
|
|
125
|
+
return output.headers[ROUTE_AFFINITY_HEADER] ?? null
|
|
54
126
|
}
|