@ottocode/sdk 0.1.197 → 0.1.199
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/package.json +1 -1
- package/src/core/src/tools/builtin/patch/apply.ts +79 -8
- package/src/core/src/tools/builtin/patch/parse.ts +1 -1
- package/src/core/src/tools/builtin/patch/repair.ts +2 -1
- package/src/core/src/tools/builtin/patch.txt +53 -166
- package/src/index.ts +2 -0
- package/src/providers/src/catalog-manual.ts +12 -0
- package/src/providers/src/catalog.ts +179 -29
- package/src/providers/src/index.ts +1 -0
- package/src/providers/src/setu-client.ts +73 -1
package/package.json
CHANGED
|
@@ -435,6 +435,7 @@ function applyHunkToLines(
|
|
|
435
435
|
let matchIndex = hasExpected
|
|
436
436
|
? findSubsequence(lines, expected, Math.max(0, initialHint - 3), useFuzzy)
|
|
437
437
|
: -1;
|
|
438
|
+
let matchedExpected = expected;
|
|
438
439
|
|
|
439
440
|
if (hasExpected && matchIndex === -1) {
|
|
440
441
|
matchIndex = findSubsequence(lines, expected, 0, useFuzzy);
|
|
@@ -447,11 +448,24 @@ function applyHunkToLines(
|
|
|
447
448
|
if (!allContextPresent) {
|
|
448
449
|
matchIndex = -1;
|
|
449
450
|
} else {
|
|
450
|
-
const expectedWithoutMissingRemovals =
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
451
|
+
const expectedWithoutMissingRemovals = hunk.lines
|
|
452
|
+
.filter((line) => {
|
|
453
|
+
if (line.kind === 'add') return false;
|
|
454
|
+
if (line.kind === 'remove') {
|
|
455
|
+
return lineExists(lines, line.content, useFuzzy);
|
|
456
|
+
}
|
|
457
|
+
return true;
|
|
458
|
+
})
|
|
459
|
+
.map((line) => line.content);
|
|
460
|
+
const includedRemovalCount = hunk.lines.filter(
|
|
461
|
+
(line) =>
|
|
462
|
+
line.kind === 'remove' && lineExists(lines, line.content, useFuzzy),
|
|
463
|
+
).length;
|
|
464
|
+
const minRequired = Math.max(contextLines.length, 2);
|
|
465
|
+
if (
|
|
466
|
+
includedRemovalCount > 0 &&
|
|
467
|
+
expectedWithoutMissingRemovals.length >= minRequired
|
|
468
|
+
) {
|
|
455
469
|
matchIndex = findSubsequence(
|
|
456
470
|
lines,
|
|
457
471
|
expectedWithoutMissingRemovals,
|
|
@@ -466,6 +480,63 @@ function applyHunkToLines(
|
|
|
466
480
|
useFuzzy,
|
|
467
481
|
);
|
|
468
482
|
}
|
|
483
|
+
if (matchIndex !== -1) {
|
|
484
|
+
matchedExpected = expectedWithoutMissingRemovals;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (
|
|
491
|
+
matchIndex === -1 &&
|
|
492
|
+
useFuzzy &&
|
|
493
|
+
removals.length >= 2 &&
|
|
494
|
+
contextLines.length === 0
|
|
495
|
+
) {
|
|
496
|
+
const firstRemoval = removals[0].content;
|
|
497
|
+
const lastRemoval = removals[removals.length - 1].content;
|
|
498
|
+
const firstIdx = findLineIndex(lines, firstRemoval, 0, true);
|
|
499
|
+
if (firstIdx !== -1) {
|
|
500
|
+
const rangeEnd = firstIdx + expected.length - 1;
|
|
501
|
+
if (rangeEnd < lines.length) {
|
|
502
|
+
const lastInRange = lines[rangeEnd];
|
|
503
|
+
let lastMatches = lastInRange === lastRemoval;
|
|
504
|
+
if (!lastMatches) {
|
|
505
|
+
for (const level of NORMALIZATION_LEVELS.slice(1)) {
|
|
506
|
+
if (
|
|
507
|
+
normalizeWhitespace(lastInRange, level) ===
|
|
508
|
+
normalizeWhitespace(lastRemoval, level)
|
|
509
|
+
) {
|
|
510
|
+
lastMatches = true;
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (lastMatches) {
|
|
516
|
+
let matchCount = 0;
|
|
517
|
+
for (let k = 0; k < expected.length; k++) {
|
|
518
|
+
const fileLine = lines[firstIdx + k];
|
|
519
|
+
const expLine = expected[k];
|
|
520
|
+
if (fileLine === expLine) {
|
|
521
|
+
matchCount++;
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
for (const level of NORMALIZATION_LEVELS.slice(1)) {
|
|
525
|
+
if (
|
|
526
|
+
normalizeWhitespace(fileLine, level) ===
|
|
527
|
+
normalizeWhitespace(expLine, level)
|
|
528
|
+
) {
|
|
529
|
+
matchCount++;
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const matchRatio = matchCount / expected.length;
|
|
535
|
+
if (matchRatio >= 0.5) {
|
|
536
|
+
matchIndex = firstIdx;
|
|
537
|
+
matchedExpected = lines.slice(firstIdx, firstIdx + expected.length);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
469
540
|
}
|
|
470
541
|
}
|
|
471
542
|
}
|
|
@@ -552,7 +623,7 @@ function applyHunkToLines(
|
|
|
552
623
|
throw new Error(errorMsg);
|
|
553
624
|
}
|
|
554
625
|
|
|
555
|
-
const deleteCount = hasExpected ?
|
|
626
|
+
const deleteCount = hasExpected ? matchedExpected.length : 0;
|
|
556
627
|
const originalIndex = matchIndex;
|
|
557
628
|
const oldStart = Math.min(
|
|
558
629
|
originalLines.length,
|
|
@@ -561,10 +632,10 @@ function applyHunkToLines(
|
|
|
561
632
|
const newStart = matchIndex + 1;
|
|
562
633
|
|
|
563
634
|
const adjustedReplacement =
|
|
564
|
-
useFuzzy && hasExpected
|
|
635
|
+
useFuzzy && hasExpected && matchedExpected.length === expected.length
|
|
565
636
|
? adjustReplacementIndentation(
|
|
566
637
|
hunk,
|
|
567
|
-
lines.slice(matchIndex, matchIndex +
|
|
638
|
+
lines.slice(matchIndex, matchIndex + matchedExpected.length),
|
|
568
639
|
originalLines,
|
|
569
640
|
)
|
|
570
641
|
: replacement;
|
|
@@ -14,7 +14,7 @@ export function parsePatchInput(patch: string): {
|
|
|
14
14
|
throw new Error('Patch content is empty.');
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
if (trimmed.
|
|
17
|
+
if (trimmed.startsWith(PATCH_BEGIN_MARKER)) {
|
|
18
18
|
return {
|
|
19
19
|
format: 'enveloped',
|
|
20
20
|
operations: parseEnvelopedPatch(patch),
|
|
@@ -37,7 +37,8 @@ function appendMissingEndMarker(patch: string): string {
|
|
|
37
37
|
const hasContent =
|
|
38
38
|
trimmed.includes('*** Update File:') ||
|
|
39
39
|
trimmed.includes('*** Add File:') ||
|
|
40
|
-
trimmed.includes('*** Delete File:')
|
|
40
|
+
trimmed.includes('*** Delete File:') ||
|
|
41
|
+
trimmed.includes('*** Replace in:');
|
|
41
42
|
|
|
42
43
|
if (hasContent) {
|
|
43
44
|
return `${trimmed}\n${PATCH_END_MARKER}`;
|
|
@@ -1,207 +1,94 @@
|
|
|
1
|
-
Apply a patch to modify one or more files.
|
|
1
|
+
Apply a patch to modify one or more files.
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
- Finished patch must include both `*** Begin Patch` and `*** End Patch`
|
|
5
|
-
- Each change needs a directive (`*** Add File`, `*** Update File`, `*** Delete File`)
|
|
6
|
-
- Include real context lines (prefixed with space) around your changes
|
|
7
|
-
- Keep paths relative to the project root
|
|
8
|
-
- **Use multiple `@@` hunks for multiple edits to the same file - do NOT make separate tool calls**
|
|
3
|
+
Use the **enveloped format** by default. Standard unified diffs (`---` / `+++`) are also accepted.
|
|
9
4
|
|
|
10
|
-
|
|
5
|
+
## Fastest / safest mode (recommended): Replace
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
Exact matching is tried first, then normalized matching if exact fails. Disable with `fuzzyMatch: false` if needed.
|
|
7
|
+
Use this when exact `+/-/context` hunks are hard to build.
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
- You want to make targeted edits to specific lines (primary use case)
|
|
17
|
-
- You want to make multiple related changes across different files in a single operation
|
|
18
|
-
- You need to add/delete entire files along with modifications
|
|
19
|
-
- You have JUST read the file immediately before (within the same response) and are confident the content hasn't changed
|
|
20
|
-
|
|
21
|
-
**CRITICAL - ALWAYS READ BEFORE PATCHING**: You MUST read the file content immediately before creating a patch.
|
|
22
|
-
Never rely on memory or previous reads. Even with fuzzy matching enabled (tolerates tabs vs spaces),
|
|
23
|
-
If the file content has changed significantly since you last read it, the patch may still fail.
|
|
24
|
-
|
|
25
|
-
**ALLOW REJECTS**: If you are applying a patch that might include stale hunks, set `allowRejects: true`.
|
|
26
|
-
The tool will apply the hunks it can and skip the rest, returning the skipped hunks with reasons.
|
|
27
|
-
|
|
28
|
-
**ALREADY APPLIED?**: If the removal lines are already gone or the additions are already present, the tool will treat the hunk as applied and move on—no need to resend the same change.
|
|
29
|
-
|
|
30
|
-
**Alternative: Use the `edit` tool if you need fuzzy matching or structured operations.**
|
|
31
|
-
|
|
32
|
-
## ⚠️ CRITICAL: Multiple Edits to Same File
|
|
33
|
-
|
|
34
|
-
**DO NOT make separate `apply_patch` calls for the same file!**
|
|
35
|
-
|
|
36
|
-
Instead, use multiple `@@` hunks in a single patch:
|
|
37
|
-
|
|
38
|
-
```
|
|
9
|
+
```text
|
|
39
10
|
*** Begin Patch
|
|
40
|
-
***
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return port;
|
|
46
|
-
}
|
|
47
|
-
@@ second change - line 25
|
|
48
|
-
function start() {
|
|
49
|
-
- console.log("Starting...");
|
|
50
|
-
+ console.log("Server starting...");
|
|
51
|
-
init();
|
|
52
|
-
}
|
|
53
|
-
@@ third change - line 40
|
|
54
|
-
function cleanup() {
|
|
55
|
-
- // TODO
|
|
56
|
-
+ console.log("Cleaning up...");
|
|
57
|
-
}
|
|
11
|
+
*** Replace in: path/to/file.ts
|
|
12
|
+
*** Find:
|
|
13
|
+
old text block
|
|
14
|
+
*** With:
|
|
15
|
+
new text block
|
|
58
16
|
*** End Patch
|
|
59
17
|
```
|
|
60
18
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
## Patch Formats
|
|
19
|
+
You can include multiple `*** Find:` / `*** With:` pairs under one `*** Replace in:` block.
|
|
64
20
|
|
|
65
|
-
|
|
21
|
+
---
|
|
66
22
|
|
|
67
|
-
|
|
23
|
+
## Standard mode: Add / Update / Delete
|
|
68
24
|
|
|
69
|
-
```
|
|
25
|
+
```text
|
|
70
26
|
*** Begin Patch
|
|
71
|
-
*** Add File: path/to/
|
|
72
|
-
+
|
|
73
|
-
|
|
74
|
-
|
|
27
|
+
*** Add File: path/to/new.txt
|
|
28
|
+
+new content
|
|
29
|
+
*** Update File: path/to/existing.ts
|
|
30
|
+
context line
|
|
75
31
|
-old line
|
|
76
32
|
+new line
|
|
77
|
-
*** Delete File: path/to/
|
|
33
|
+
*** Delete File: path/to/old.txt
|
|
78
34
|
*** End Patch
|
|
79
35
|
```
|
|
80
36
|
|
|
81
|
-
|
|
37
|
+
For multiple edits in the same file, use **one** `*** Update File:` block with multiple `@@` hunks.
|
|
82
38
|
|
|
83
|
-
|
|
39
|
+
---
|
|
84
40
|
|
|
85
|
-
|
|
86
|
-
diff --git a/src/app.ts b/src/app.ts
|
|
87
|
-
--- a/src/app.ts
|
|
88
|
-
+++ b/src/app.ts
|
|
89
|
-
@@
|
|
90
|
-
-const PORT = 3000;
|
|
91
|
-
+const PORT = 8080;
|
|
92
|
-
```
|
|
41
|
+
## Rules that prevent failures
|
|
93
42
|
|
|
94
|
-
|
|
43
|
+
1. Patch must start with `*** Begin Patch` and end with `*** End Patch`.
|
|
44
|
+
2. Paths must be relative to project root.
|
|
45
|
+
3. In `Update File`, `-` lines must match file content exactly.
|
|
46
|
+
4. Include real context lines (prefix with a single space ` `).
|
|
47
|
+
5. `@@` lines are only hints; they do not replace real context.
|
|
48
|
+
6. Do not make separate `apply_patch` calls for the same file in one task.
|
|
95
49
|
|
|
96
|
-
|
|
50
|
+
---
|
|
97
51
|
|
|
98
|
-
|
|
99
|
-
```
|
|
100
|
-
*** Begin Patch
|
|
101
|
-
*** Add File: src/hello.ts
|
|
102
|
-
+export function hello() {
|
|
103
|
-
+ console.log("Hello!");
|
|
104
|
-
+}
|
|
105
|
-
*** End Patch
|
|
106
|
-
```
|
|
52
|
+
## Matching behavior
|
|
107
53
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
*** Update File: src/config.ts
|
|
112
|
-
-const PORT = 3000;
|
|
113
|
-
+const PORT = 8080;
|
|
114
|
-
*** End Patch
|
|
115
|
-
```
|
|
54
|
+
- `fuzzyMatch` defaults to `true` (helps with small whitespace differences).
|
|
55
|
+
- Set `fuzzyMatch: false` for strict matching.
|
|
56
|
+
- If a hunk may be stale, set `allowRejects: true` to apply valid parts and skip invalid ones.
|
|
116
57
|
|
|
117
|
-
|
|
58
|
+
---
|
|
118
59
|
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
*** Begin Patch
|
|
122
|
-
*** Update File: src/app.ts
|
|
123
|
-
@@ function main()
|
|
124
|
-
function main() {
|
|
125
|
-
- console.log("old");
|
|
126
|
-
+ console.log("new");
|
|
127
|
-
}
|
|
128
|
-
*** End Patch
|
|
129
|
-
```
|
|
60
|
+
## Common errors
|
|
130
61
|
|
|
131
|
-
**
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
- The context lines with space prefix are what the tool uses to find the location
|
|
135
|
-
- The `@@` line just helps humans/AI understand what section you're editing
|
|
62
|
+
- **Missing end marker** → add `*** End Patch`.
|
|
63
|
+
- **Failed to find expected lines** → re-read the file and rebuild patch with exact context.
|
|
64
|
+
- **Ambiguous update** → provide longer context or use `*** Replace in:` mode.
|
|
136
65
|
|
|
66
|
+
---
|
|
137
67
|
|
|
138
|
-
|
|
139
|
-
|
|
68
|
+
## Minimal templates
|
|
69
|
+
|
|
70
|
+
### Update one line
|
|
71
|
+
```text
|
|
140
72
|
*** Begin Patch
|
|
141
73
|
*** Update File: src/app.ts
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
+ const port = 8080;
|
|
146
|
-
return port;
|
|
147
|
-
}
|
|
148
|
-
@@ second section - near line 25
|
|
149
|
-
function start() {
|
|
150
|
-
- console.log("Starting...");
|
|
151
|
-
+ console.log("Server starting...");
|
|
152
|
-
init();
|
|
74
|
+
function main() {
|
|
75
|
+
- console.log("old");
|
|
76
|
+
+ console.log("new");
|
|
153
77
|
}
|
|
154
78
|
*** End Patch
|
|
155
79
|
```
|
|
156
80
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
### Delete a file:
|
|
160
|
-
```
|
|
81
|
+
### Add file
|
|
82
|
+
```text
|
|
161
83
|
*** Begin Patch
|
|
162
|
-
***
|
|
84
|
+
*** Add File: src/new.ts
|
|
85
|
+
+export const ok = true;
|
|
163
86
|
*** End Patch
|
|
164
87
|
```
|
|
165
88
|
|
|
166
|
-
###
|
|
167
|
-
```
|
|
89
|
+
### Delete file
|
|
90
|
+
```text
|
|
168
91
|
*** Begin Patch
|
|
169
|
-
***
|
|
170
|
-
+New content
|
|
171
|
-
*** Update File: existing.txt
|
|
172
|
-
-old
|
|
173
|
-
+new
|
|
174
|
-
*** Delete File: obsolete.txt
|
|
92
|
+
*** Delete File: src/obsolete.ts
|
|
175
93
|
*** End Patch
|
|
176
94
|
```
|
|
177
|
-
|
|
178
|
-
## Line Prefixes
|
|
179
|
-
|
|
180
|
-
- Lines starting with `+` are added
|
|
181
|
-
- Lines starting with `-` are removed
|
|
182
|
-
- Lines starting with ` ` (space) are context (kept unchanged)
|
|
183
|
-
- Lines starting with `@@` are optional hints/comments (not parsed as context)
|
|
184
|
-
|
|
185
|
-
## Common Errors
|
|
186
|
-
|
|
187
|
-
**"Missing *** End Patch marker"**: Every patch MUST end with `*** End Patch` on its own line.
|
|
188
|
-
This is the most common error. Double-check your patch ends with:
|
|
189
|
-
```
|
|
190
|
-
*** End Patch
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
**"Failed to find expected lines"**: The file content doesn't match your patch. Common causes:
|
|
194
|
-
- Missing context lines (lines with space prefix)
|
|
195
|
-
- Using `@@` line as context instead of real context lines
|
|
196
|
-
- The file content has changed since you read it
|
|
197
|
-
- Whitespace/indentation mismatch
|
|
198
|
-
|
|
199
|
-
**Solution**: Always read the file immediately before patching and include actual context lines with space prefix.
|
|
200
|
-
|
|
201
|
-
## Important Notes
|
|
202
|
-
|
|
203
|
-
- **Patches are fragile**: Any mismatch in whitespace, indentation, or content will cause failure
|
|
204
|
-
- **Use `edit` for reliability**: The `edit` tool can make targeted changes without requiring exact matches
|
|
205
|
-
- All file paths are relative to the project root
|
|
206
|
-
- The enveloped format is the most reliable; unified diffs are converted automatically if provided
|
|
207
|
-
- Always wrap patches with `*** Begin Patch` and `*** End Patch` when you write them manually
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,12 @@ const isAllowedGoogleModel = (id: string): boolean => {
|
|
|
30
30
|
return false;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
const isAllowedZaiModel = (id: string): boolean => {
|
|
34
|
+
if (id.startsWith('glm-4.7')) return true;
|
|
35
|
+
if (id.startsWith('glm-5')) return true;
|
|
36
|
+
return false;
|
|
37
|
+
};
|
|
38
|
+
|
|
33
39
|
const SETU_SOURCES: Array<{
|
|
34
40
|
id: ProviderId;
|
|
35
41
|
npm: string;
|
|
@@ -55,6 +61,11 @@ const SETU_SOURCES: Array<{
|
|
|
55
61
|
npm: '@ai-sdk/google',
|
|
56
62
|
family: 'google',
|
|
57
63
|
},
|
|
64
|
+
{
|
|
65
|
+
id: 'zai',
|
|
66
|
+
npm: '@ai-sdk/openai-compatible',
|
|
67
|
+
family: 'openai-compatible',
|
|
68
|
+
},
|
|
58
69
|
];
|
|
59
70
|
|
|
60
71
|
function cloneModel(model: ModelInfo): ModelInfo {
|
|
@@ -83,6 +94,7 @@ function buildSetuEntry(base: CatalogMap): ProviderCatalogEntry | null {
|
|
|
83
94
|
if (id === 'openai') return isAllowedOpenAIModel(model.id);
|
|
84
95
|
if (id === 'anthropic') return isAllowedAnthropicModel(model.id);
|
|
85
96
|
if (id === 'google') return isAllowedGoogleModel(model.id);
|
|
97
|
+
if (id === 'zai') return isAllowedZaiModel(model.id);
|
|
86
98
|
return true;
|
|
87
99
|
});
|
|
88
100
|
return sourceModels.map((model) => {
|
|
@@ -703,6 +703,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
703
703
|
output: 128000,
|
|
704
704
|
},
|
|
705
705
|
},
|
|
706
|
+
{
|
|
707
|
+
id: 'gpt-5.3-codex-spark',
|
|
708
|
+
label: 'GPT-5.3 Codex Spark',
|
|
709
|
+
modalities: {
|
|
710
|
+
input: ['text', 'image', 'pdf'],
|
|
711
|
+
output: ['text'],
|
|
712
|
+
},
|
|
713
|
+
toolCall: true,
|
|
714
|
+
reasoningText: true,
|
|
715
|
+
attachment: true,
|
|
716
|
+
temperature: false,
|
|
717
|
+
knowledge: '2025-08-31',
|
|
718
|
+
releaseDate: '2026-02-05',
|
|
719
|
+
lastUpdated: '2026-02-05',
|
|
720
|
+
openWeights: false,
|
|
721
|
+
cost: {
|
|
722
|
+
input: 1.75,
|
|
723
|
+
output: 14,
|
|
724
|
+
cacheRead: 0.175,
|
|
725
|
+
},
|
|
726
|
+
limit: {
|
|
727
|
+
context: 128000,
|
|
728
|
+
output: 32000,
|
|
729
|
+
},
|
|
730
|
+
},
|
|
706
731
|
{
|
|
707
732
|
id: 'o1',
|
|
708
733
|
label: 'o1',
|
|
@@ -5229,29 +5254,6 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
5229
5254
|
output: 100000,
|
|
5230
5255
|
},
|
|
5231
5256
|
},
|
|
5232
|
-
{
|
|
5233
|
-
id: 'openrouter/pony-alpha',
|
|
5234
|
-
label: 'Pony Alpha',
|
|
5235
|
-
modalities: {
|
|
5236
|
-
input: ['text'],
|
|
5237
|
-
output: ['text'],
|
|
5238
|
-
},
|
|
5239
|
-
toolCall: true,
|
|
5240
|
-
reasoningText: true,
|
|
5241
|
-
attachment: false,
|
|
5242
|
-
temperature: true,
|
|
5243
|
-
releaseDate: '2026-02-06',
|
|
5244
|
-
lastUpdated: '2026-02-06',
|
|
5245
|
-
openWeights: false,
|
|
5246
|
-
cost: {
|
|
5247
|
-
input: 0,
|
|
5248
|
-
output: 0,
|
|
5249
|
-
},
|
|
5250
|
-
limit: {
|
|
5251
|
-
context: 200000,
|
|
5252
|
-
output: 131000,
|
|
5253
|
-
},
|
|
5254
|
-
},
|
|
5255
5257
|
{
|
|
5256
5258
|
id: 'openrouter/sherlock-dash-alpha',
|
|
5257
5259
|
label: 'Sherlock Dash Alpha',
|
|
@@ -6043,6 +6045,55 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
6043
6045
|
output: 8192,
|
|
6044
6046
|
},
|
|
6045
6047
|
},
|
|
6048
|
+
{
|
|
6049
|
+
id: 'stepfun/step-3.5-flash',
|
|
6050
|
+
label: 'Step 3.5 Flash',
|
|
6051
|
+
modalities: {
|
|
6052
|
+
input: ['text'],
|
|
6053
|
+
output: ['text'],
|
|
6054
|
+
},
|
|
6055
|
+
toolCall: true,
|
|
6056
|
+
reasoningText: true,
|
|
6057
|
+
attachment: false,
|
|
6058
|
+
temperature: true,
|
|
6059
|
+
knowledge: '2025-01',
|
|
6060
|
+
releaseDate: '2026-01-29',
|
|
6061
|
+
lastUpdated: '2026-01-29',
|
|
6062
|
+
openWeights: true,
|
|
6063
|
+
cost: {
|
|
6064
|
+
input: 0.1,
|
|
6065
|
+
output: 0.3,
|
|
6066
|
+
cacheRead: 0.02,
|
|
6067
|
+
},
|
|
6068
|
+
limit: {
|
|
6069
|
+
context: 256000,
|
|
6070
|
+
output: 256000,
|
|
6071
|
+
},
|
|
6072
|
+
},
|
|
6073
|
+
{
|
|
6074
|
+
id: 'stepfun/step-3.5-flash:free',
|
|
6075
|
+
label: 'Step 3.5 Flash (free)',
|
|
6076
|
+
modalities: {
|
|
6077
|
+
input: ['text'],
|
|
6078
|
+
output: ['text'],
|
|
6079
|
+
},
|
|
6080
|
+
toolCall: true,
|
|
6081
|
+
reasoningText: true,
|
|
6082
|
+
attachment: false,
|
|
6083
|
+
temperature: true,
|
|
6084
|
+
knowledge: '2025-01',
|
|
6085
|
+
releaseDate: '2026-01-29',
|
|
6086
|
+
lastUpdated: '2026-01-29',
|
|
6087
|
+
openWeights: true,
|
|
6088
|
+
cost: {
|
|
6089
|
+
input: 0,
|
|
6090
|
+
output: 0,
|
|
6091
|
+
},
|
|
6092
|
+
limit: {
|
|
6093
|
+
context: 256000,
|
|
6094
|
+
output: 256000,
|
|
6095
|
+
},
|
|
6096
|
+
},
|
|
6046
6097
|
{
|
|
6047
6098
|
id: 'thudm/glm-z1-32b:free',
|
|
6048
6099
|
label: 'GLM Z1 32B (free)',
|
|
@@ -6541,6 +6592,30 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
6541
6592
|
output: 65535,
|
|
6542
6593
|
},
|
|
6543
6594
|
},
|
|
6595
|
+
{
|
|
6596
|
+
id: 'z-ai/glm-5',
|
|
6597
|
+
label: 'GLM-5',
|
|
6598
|
+
modalities: {
|
|
6599
|
+
input: ['text'],
|
|
6600
|
+
output: ['text'],
|
|
6601
|
+
},
|
|
6602
|
+
toolCall: true,
|
|
6603
|
+
reasoningText: true,
|
|
6604
|
+
attachment: false,
|
|
6605
|
+
temperature: true,
|
|
6606
|
+
releaseDate: '2026-02-12',
|
|
6607
|
+
lastUpdated: '2026-02-12',
|
|
6608
|
+
openWeights: true,
|
|
6609
|
+
cost: {
|
|
6610
|
+
input: 1,
|
|
6611
|
+
output: 3.2,
|
|
6612
|
+
cacheRead: 0.2,
|
|
6613
|
+
},
|
|
6614
|
+
limit: {
|
|
6615
|
+
context: 202752,
|
|
6616
|
+
output: 131000,
|
|
6617
|
+
},
|
|
6618
|
+
},
|
|
6544
6619
|
],
|
|
6545
6620
|
label: 'OpenRouter',
|
|
6546
6621
|
env: ['OPENROUTER_API_KEY'],
|
|
@@ -7341,6 +7416,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
7341
7416
|
npm: '@ai-sdk/anthropic',
|
|
7342
7417
|
},
|
|
7343
7418
|
},
|
|
7419
|
+
{
|
|
7420
|
+
id: 'minimax-m2.5-free',
|
|
7421
|
+
label: 'MiniMax M2.5 Free',
|
|
7422
|
+
modalities: {
|
|
7423
|
+
input: ['text'],
|
|
7424
|
+
output: ['text'],
|
|
7425
|
+
},
|
|
7426
|
+
toolCall: true,
|
|
7427
|
+
reasoningText: true,
|
|
7428
|
+
attachment: false,
|
|
7429
|
+
temperature: true,
|
|
7430
|
+
knowledge: '2025-01',
|
|
7431
|
+
releaseDate: '2026-02-12',
|
|
7432
|
+
lastUpdated: '2026-02-12',
|
|
7433
|
+
openWeights: true,
|
|
7434
|
+
cost: {
|
|
7435
|
+
input: 0,
|
|
7436
|
+
output: 0,
|
|
7437
|
+
cacheRead: 0,
|
|
7438
|
+
},
|
|
7439
|
+
limit: {
|
|
7440
|
+
context: 204800,
|
|
7441
|
+
output: 131072,
|
|
7442
|
+
},
|
|
7443
|
+
},
|
|
7344
7444
|
{
|
|
7345
7445
|
id: 'qwen3-coder',
|
|
7346
7446
|
label: 'Qwen3 Coder',
|
|
@@ -7603,6 +7703,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
7603
7703
|
output: 131072,
|
|
7604
7704
|
},
|
|
7605
7705
|
},
|
|
7706
|
+
{
|
|
7707
|
+
id: 'glm-5',
|
|
7708
|
+
label: 'GLM-5',
|
|
7709
|
+
modalities: {
|
|
7710
|
+
input: ['text'],
|
|
7711
|
+
output: ['text'],
|
|
7712
|
+
},
|
|
7713
|
+
toolCall: true,
|
|
7714
|
+
reasoningText: true,
|
|
7715
|
+
attachment: false,
|
|
7716
|
+
temperature: true,
|
|
7717
|
+
releaseDate: '2026-02-11',
|
|
7718
|
+
lastUpdated: '2026-02-11',
|
|
7719
|
+
openWeights: false,
|
|
7720
|
+
cost: {
|
|
7721
|
+
input: 1,
|
|
7722
|
+
output: 3.2,
|
|
7723
|
+
cacheRead: 0.2,
|
|
7724
|
+
cacheWrite: 0,
|
|
7725
|
+
},
|
|
7726
|
+
limit: {
|
|
7727
|
+
context: 204800,
|
|
7728
|
+
output: 131072,
|
|
7729
|
+
},
|
|
7730
|
+
},
|
|
7606
7731
|
],
|
|
7607
7732
|
label: 'Z.AI',
|
|
7608
7733
|
env: ['ZHIPU_API_KEY'],
|
|
@@ -7817,6 +7942,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
7817
7942
|
output: 131072,
|
|
7818
7943
|
},
|
|
7819
7944
|
},
|
|
7945
|
+
{
|
|
7946
|
+
id: 'glm-5',
|
|
7947
|
+
label: 'GLM-5',
|
|
7948
|
+
modalities: {
|
|
7949
|
+
input: ['text'],
|
|
7950
|
+
output: ['text'],
|
|
7951
|
+
},
|
|
7952
|
+
toolCall: true,
|
|
7953
|
+
reasoningText: true,
|
|
7954
|
+
attachment: false,
|
|
7955
|
+
temperature: true,
|
|
7956
|
+
releaseDate: '2026-02-11',
|
|
7957
|
+
lastUpdated: '2026-02-11',
|
|
7958
|
+
openWeights: false,
|
|
7959
|
+
cost: {
|
|
7960
|
+
input: 0,
|
|
7961
|
+
output: 0,
|
|
7962
|
+
cacheRead: 0,
|
|
7963
|
+
cacheWrite: 0,
|
|
7964
|
+
},
|
|
7965
|
+
limit: {
|
|
7966
|
+
context: 204800,
|
|
7967
|
+
output: 131072,
|
|
7968
|
+
},
|
|
7969
|
+
},
|
|
7820
7970
|
],
|
|
7821
7971
|
label: 'Z.AI Coding Plan',
|
|
7822
7972
|
env: ['ZHIPU_API_KEY'],
|
|
@@ -8008,7 +8158,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8008
8158
|
},
|
|
8009
8159
|
limit: {
|
|
8010
8160
|
context: 128000,
|
|
8011
|
-
output:
|
|
8161
|
+
output: 32000,
|
|
8012
8162
|
},
|
|
8013
8163
|
},
|
|
8014
8164
|
{
|
|
@@ -8032,7 +8182,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8032
8182
|
},
|
|
8033
8183
|
limit: {
|
|
8034
8184
|
context: 128000,
|
|
8035
|
-
output:
|
|
8185
|
+
output: 32000,
|
|
8036
8186
|
},
|
|
8037
8187
|
},
|
|
8038
8188
|
{
|
|
@@ -8128,7 +8278,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8128
8278
|
},
|
|
8129
8279
|
limit: {
|
|
8130
8280
|
context: 128000,
|
|
8131
|
-
output:
|
|
8281
|
+
output: 32000,
|
|
8132
8282
|
},
|
|
8133
8283
|
},
|
|
8134
8284
|
{
|
|
@@ -8223,7 +8373,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8223
8373
|
output: 0,
|
|
8224
8374
|
},
|
|
8225
8375
|
limit: {
|
|
8226
|
-
context:
|
|
8376
|
+
context: 64000,
|
|
8227
8377
|
output: 16384,
|
|
8228
8378
|
},
|
|
8229
8379
|
},
|
|
@@ -8320,7 +8470,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8320
8470
|
},
|
|
8321
8471
|
limit: {
|
|
8322
8472
|
context: 128000,
|
|
8323
|
-
output:
|
|
8473
|
+
output: 64000,
|
|
8324
8474
|
},
|
|
8325
8475
|
},
|
|
8326
8476
|
{
|
|
@@ -8392,7 +8542,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8392
8542
|
},
|
|
8393
8543
|
limit: {
|
|
8394
8544
|
context: 128000,
|
|
8395
|
-
output:
|
|
8545
|
+
output: 128000,
|
|
8396
8546
|
},
|
|
8397
8547
|
},
|
|
8398
8548
|
{
|
|
@@ -44,6 +44,13 @@ const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';
|
|
|
44
44
|
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
45
45
|
const DEFAULT_MAX_PAYMENT_ATTEMPTS = 20;
|
|
46
46
|
|
|
47
|
+
export type SetuBalanceUpdate = {
|
|
48
|
+
costUsd: number;
|
|
49
|
+
balanceRemaining: number;
|
|
50
|
+
inputTokens?: number;
|
|
51
|
+
outputTokens?: number;
|
|
52
|
+
};
|
|
53
|
+
|
|
47
54
|
export type SetuPaymentCallbacks = {
|
|
48
55
|
onPaymentRequired?: (amountUsd: number, currentBalance?: number) => void;
|
|
49
56
|
onPaymentSigning?: () => void;
|
|
@@ -57,6 +64,7 @@ export type SetuPaymentCallbacks = {
|
|
|
57
64
|
amountUsd: number;
|
|
58
65
|
currentBalance: number;
|
|
59
66
|
}) => Promise<'crypto' | 'fiat' | 'cancel'>;
|
|
67
|
+
onBalanceUpdate?: (update: SetuBalanceUpdate) => void;
|
|
60
68
|
};
|
|
61
69
|
|
|
62
70
|
export type SetuProviderOptions = {
|
|
@@ -197,7 +205,7 @@ export function createSetuFetch(
|
|
|
197
205
|
const response = await baseFetch(input, { ...init, body, headers });
|
|
198
206
|
|
|
199
207
|
if (response.status !== 402) {
|
|
200
|
-
return response;
|
|
208
|
+
return wrapResponseWithBalanceSniffing(response, callbacks);
|
|
201
209
|
}
|
|
202
210
|
|
|
203
211
|
const payload = await response.json().catch(() => ({}));
|
|
@@ -266,6 +274,70 @@ export function createSetuFetch(
|
|
|
266
274
|
};
|
|
267
275
|
}
|
|
268
276
|
|
|
277
|
+
function tryParseSetuComment(
|
|
278
|
+
line: string,
|
|
279
|
+
onBalanceUpdate: (update: SetuBalanceUpdate) => void,
|
|
280
|
+
) {
|
|
281
|
+
const trimmed = line.replace(/\r$/, '');
|
|
282
|
+
if (!trimmed.startsWith(': setu ')) return;
|
|
283
|
+
try {
|
|
284
|
+
const data = JSON.parse(trimmed.slice(7));
|
|
285
|
+
onBalanceUpdate({
|
|
286
|
+
costUsd: parseFloat(data.cost_usd ?? '0'),
|
|
287
|
+
balanceRemaining: parseFloat(data.balance_remaining ?? '0'),
|
|
288
|
+
inputTokens: data.input_tokens ? Number(data.input_tokens) : undefined,
|
|
289
|
+
outputTokens: data.output_tokens ? Number(data.output_tokens) : undefined,
|
|
290
|
+
});
|
|
291
|
+
} catch {}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function wrapResponseWithBalanceSniffing(
|
|
295
|
+
response: Response,
|
|
296
|
+
callbacks: SetuPaymentCallbacks,
|
|
297
|
+
): Response {
|
|
298
|
+
if (!callbacks.onBalanceUpdate) return response;
|
|
299
|
+
|
|
300
|
+
const balanceHeader = response.headers.get('x-balance-remaining');
|
|
301
|
+
const costHeader = response.headers.get('x-cost-usd');
|
|
302
|
+
if (balanceHeader && costHeader) {
|
|
303
|
+
callbacks.onBalanceUpdate({
|
|
304
|
+
costUsd: parseFloat(costHeader),
|
|
305
|
+
balanceRemaining: parseFloat(balanceHeader),
|
|
306
|
+
});
|
|
307
|
+
return response;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!response.body) return response;
|
|
311
|
+
|
|
312
|
+
const onBalanceUpdate = callbacks.onBalanceUpdate;
|
|
313
|
+
let partial = '';
|
|
314
|
+
const decoder = new TextDecoder();
|
|
315
|
+
const transform = new TransformStream<Uint8Array, Uint8Array>({
|
|
316
|
+
transform(chunk, controller) {
|
|
317
|
+
controller.enqueue(chunk);
|
|
318
|
+
partial += decoder.decode(chunk, { stream: true });
|
|
319
|
+
let nlIndex = partial.indexOf('\n');
|
|
320
|
+
while (nlIndex !== -1) {
|
|
321
|
+
const line = partial.slice(0, nlIndex);
|
|
322
|
+
partial = partial.slice(nlIndex + 1);
|
|
323
|
+
tryParseSetuComment(line, onBalanceUpdate);
|
|
324
|
+
nlIndex = partial.indexOf('\n');
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
flush() {
|
|
328
|
+
if (partial.trim()) {
|
|
329
|
+
tryParseSetuComment(partial, onBalanceUpdate);
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
return new Response(response.body.pipeThrough(transform), {
|
|
335
|
+
status: response.status,
|
|
336
|
+
statusText: response.statusText,
|
|
337
|
+
headers: response.headers,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
269
341
|
/**
|
|
270
342
|
* Create a Setu-backed AI model.
|
|
271
343
|
*
|