cdsa-harness 0.22.3 → 0.22.5
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/builtins.js +1 -1
- package/src/cli.js +6 -1
- package/src/llm.js +41 -4
package/package.json
CHANGED
package/src/builtins.js
CHANGED
package/src/cli.js
CHANGED
|
@@ -297,7 +297,12 @@ async function fetchProviderModels(cfg, query = "") {
|
|
|
297
297
|
try {
|
|
298
298
|
if (cfg.provider === "openrouter") {
|
|
299
299
|
const r = await fetchJson("https://openrouter.ai/api/v1/models");
|
|
300
|
-
|
|
300
|
+
// 하네스는 항상 도구(tools)를 함께 보내므로, 도구를 지원하지 않는 모델은 어차피
|
|
301
|
+
// "No endpoints found that support tool use" 로 실패한다 — 목록에서 미리 제외.
|
|
302
|
+
let ids = (r.data || [])
|
|
303
|
+
.filter((m) => !Array.isArray(m.supported_parameters) || m.supported_parameters.includes("tools"))
|
|
304
|
+
.map((m) => m.id)
|
|
305
|
+
.sort();
|
|
301
306
|
if (q) ids = ids.filter((id) => id.toLowerCase().includes(q));
|
|
302
307
|
return { list: ids, live: true };
|
|
303
308
|
}
|
package/src/llm.js
CHANGED
|
@@ -46,6 +46,18 @@ export class LLMClient {
|
|
|
46
46
|
throw new LLMError(`지원하지 않는 provider 입니다: ${this.provider}`);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// _post/_openStream 을 감싸, 샘플링 파라미터 거부(400)를 만나면 빼고 1회 재시도.
|
|
50
|
+
async _sendWithSamplingRetry(send, body) {
|
|
51
|
+
if (this._noSampling) this._stripSampling(body);
|
|
52
|
+
try {
|
|
53
|
+
return await send(body);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
if (!this._shouldRetryWithoutSampling(e, body)) throw e;
|
|
56
|
+
this._stripSampling(body);
|
|
57
|
+
return await send(body);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
// 스트리밍용: 응답 객체(본문 스트림)를 그대로 받는다.
|
|
50
62
|
async _openStream(url, headers, body) {
|
|
51
63
|
const json = JSON.stringify(body);
|
|
@@ -71,6 +83,22 @@ export class LLMClient {
|
|
|
71
83
|
return describeNetError(e, this.provider, this.timeout, url);
|
|
72
84
|
}
|
|
73
85
|
|
|
86
|
+
// 최신 Claude 모델(Sonnet 5, Opus 4.7+ 등)은 temperature/top_p/top_k 샘플링 파라미터를
|
|
87
|
+
// 거부한다(400: "`temperature` is deprecated for this model."). 모델 목록을 하드코딩하는
|
|
88
|
+
// 대신 그 400 을 감지하면 파라미터를 빼고 1회 재시도하고, 세션 동안 기억해 다시 안 보낸다.
|
|
89
|
+
_shouldRetryWithoutSampling(e, body) {
|
|
90
|
+
if (this._noSampling) return false;
|
|
91
|
+
if (!("temperature" in body || "top_p" in body || "top_k" in body)) return false;
|
|
92
|
+
return e instanceof LLMError && /API 오류 400/.test(e.message) && /temperature|top_p|top_k/.test(e.message);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_stripSampling(body) {
|
|
96
|
+
this._noSampling = true;
|
|
97
|
+
delete body.temperature;
|
|
98
|
+
delete body.top_p;
|
|
99
|
+
delete body.top_k;
|
|
100
|
+
}
|
|
101
|
+
|
|
74
102
|
async _post(url, headers, body) {
|
|
75
103
|
const json = JSON.stringify(body);
|
|
76
104
|
const ctrl = new AbortController();
|
|
@@ -108,7 +136,7 @@ export class LLMClient {
|
|
|
108
136
|
headers["HTTP-Referer"] = "https://github.com/cdsassj00/miniharness";
|
|
109
137
|
headers["X-Title"] = "CDSA Harness";
|
|
110
138
|
}
|
|
111
|
-
const { payload, latencyMs, bodyBytes } = await this._post(url, headers, body);
|
|
139
|
+
const { payload, latencyMs, bodyBytes } = await this._sendWithSamplingRetry((b) => this._post(url, headers, b), body);
|
|
112
140
|
const parsed = parseOpenAiReply(payload);
|
|
113
141
|
return {
|
|
114
142
|
...parsed,
|
|
@@ -126,7 +154,7 @@ export class LLMClient {
|
|
|
126
154
|
"anthropic-version": "2023-06-01",
|
|
127
155
|
"Content-Type": "application/json",
|
|
128
156
|
};
|
|
129
|
-
const { payload, latencyMs, bodyBytes } = await this._post(url, headers, body);
|
|
157
|
+
const { payload, latencyMs, bodyBytes } = await this._sendWithSamplingRetry((b) => this._post(url, headers, b), body);
|
|
130
158
|
const parsed = parseAnthropicReply(payload);
|
|
131
159
|
return {
|
|
132
160
|
...parsed,
|
|
@@ -148,7 +176,7 @@ export class LLMClient {
|
|
|
148
176
|
headers["HTTP-Referer"] = "https://github.com/cdsassj00/miniharness";
|
|
149
177
|
headers["X-Title"] = "CDSA Harness";
|
|
150
178
|
}
|
|
151
|
-
const { res, started, timer, bodyBytes } = await this._openStream(url, headers, body);
|
|
179
|
+
const { res, started, timer, bodyBytes } = await this._sendWithSamplingRetry((b) => this._openStream(url, headers, b), body);
|
|
152
180
|
let content = "";
|
|
153
181
|
const tcMap = new Map(); // index -> {id,name,args}
|
|
154
182
|
let usage = null;
|
|
@@ -183,7 +211,7 @@ export class LLMClient {
|
|
|
183
211
|
const body = toAnthropicBody(messages, tools, this.model, this.temperature, this.maxTokens);
|
|
184
212
|
body.stream = true;
|
|
185
213
|
const headers = { "x-api-key": this.apiKey, "anthropic-version": "2023-06-01", "Content-Type": "application/json" };
|
|
186
|
-
const { res, started, timer, bodyBytes } = await this._openStream(url, headers, body);
|
|
214
|
+
const { res, started, timer, bodyBytes } = await this._sendWithSamplingRetry((b) => this._openStream(url, headers, b), body);
|
|
187
215
|
let content = "";
|
|
188
216
|
const blocks = new Map(); // index -> {type,name,id,json}
|
|
189
217
|
let usage = { input: null, output: null, total: 0 };
|
|
@@ -318,6 +346,15 @@ function safeParse(jsonStr) {
|
|
|
318
346
|
|
|
319
347
|
function httpErrorMessage(status, text) {
|
|
320
348
|
let msg = `API 오류 ${status}: ${trim(text)}`;
|
|
349
|
+
// 도구(tool use) 미지원 모델/엔드포인트 — 모델명 오타 힌트는 오답이므로 여기서 끝낸다.
|
|
350
|
+
if (/tool[ _]use|support tools?/i.test(text)) {
|
|
351
|
+
msg +=
|
|
352
|
+
"\n ↳ 이 모델(또는 이 모델의 제공 엔드포인트)이 도구 호출(tool use)을 지원하지 않아요. " +
|
|
353
|
+
"에이전트 하네스는 파일 도구가 필수라 도구 지원 모델이 필요합니다." +
|
|
354
|
+
"\n ↳ /models 로 다른 모델을 고르세요 — OpenRouter 목록은 도구 지원 모델만 표시됩니다 " +
|
|
355
|
+
"(예: anthropic/claude-sonnet-4.5, openai/gpt-4o-mini).";
|
|
356
|
+
return msg;
|
|
357
|
+
}
|
|
321
358
|
if (status === 404) {
|
|
322
359
|
msg += "\n ↳ 모델 이름을 확인하세요. /model 로 변경 가능. " +
|
|
323
360
|
"OpenRouter 는 'provider/model' 형식이어야 합니다 (예: openai/gpt-4o-mini, anthropic/claude-3.7-sonnet).";
|