@cogcoin/client 1.1.1 → 1.1.3
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/wallet/mining/control.js +18 -3
- package/dist/wallet/mining/runner.d.ts +116 -1
- package/dist/wallet/mining/runner.js +372 -110
- package/dist/wallet/mining/sentences.js +117 -12
- package/dist/wallet/mining/visualizer.d.ts +1 -0
- package/dist/wallet/mining/visualizer.js +13 -0
- package/package.json +1 -1
|
@@ -18,6 +18,14 @@ function createBuiltInProviderNotFoundError(options) {
|
|
|
18
18
|
: `${providerLabel} returned HTTP 404 for model "${options.model}". The configured model override may be invalid. Rerun \`cogcoin mine setup\` to clear or correct it.`;
|
|
19
19
|
return new MiningProviderRequestError("not-found", message);
|
|
20
20
|
}
|
|
21
|
+
function createBuiltInProviderTimeoutError(options) {
|
|
22
|
+
const providerName = options.provider === "anthropic" ? "Anthropic" : "OpenAI";
|
|
23
|
+
const seconds = Number.isInteger(options.timeoutMs / 1_000)
|
|
24
|
+
? String(options.timeoutMs / 1_000)
|
|
25
|
+
: (options.timeoutMs / 1_000).toFixed(1);
|
|
26
|
+
const unit = seconds === "1" ? "second" : "seconds";
|
|
27
|
+
return new MiningProviderRequestError("unavailable", `The built-in ${providerName} mining provider timed out after ${seconds} ${unit}.`);
|
|
28
|
+
}
|
|
21
29
|
function buildSystemPrompt(extraPrompt) {
|
|
22
30
|
const lines = [
|
|
23
31
|
"You are helping generate candidate Cogcoin mining sentences.",
|
|
@@ -57,25 +65,89 @@ function annotateProviderCandidates(options) {
|
|
|
57
65
|
},
|
|
58
66
|
}));
|
|
59
67
|
}
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
const ANTHROPIC_MINING_RESPONSE_TOOL_NAME = "return_mining_candidates";
|
|
69
|
+
const ANTHROPIC_MINING_RESPONSE_TOOL = {
|
|
70
|
+
name: ANTHROPIC_MINING_RESPONSE_TOOL_NAME,
|
|
71
|
+
description: [
|
|
72
|
+
"Return the Cogcoin mining sentence generation result in structured form.",
|
|
73
|
+
"Use this tool exactly once instead of writing prose, markdown, or code fences.",
|
|
74
|
+
"Set schemaVersion to 1, copy the requestId exactly, and include only candidates for domainId values from rootDomains.",
|
|
75
|
+
"Each candidate sentence must be a single natural-language sentence with no surrounding commentary.",
|
|
76
|
+
].join(" "),
|
|
77
|
+
input_schema: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
schemaVersion: { type: "integer" },
|
|
81
|
+
requestId: { type: "string" },
|
|
82
|
+
candidates: {
|
|
83
|
+
type: "array",
|
|
84
|
+
items: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
domainId: { type: "integer" },
|
|
88
|
+
sentence: { type: "string" },
|
|
89
|
+
},
|
|
90
|
+
required: ["domainId", "sentence"],
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
required: ["schemaVersion", "requestId", "candidates"],
|
|
96
|
+
additionalProperties: false,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
function normalizeProviderCandidateResponse(options) {
|
|
62
100
|
try {
|
|
63
101
|
return normalizeMiningSentenceResponse({
|
|
64
102
|
request: options.request,
|
|
65
|
-
response,
|
|
103
|
+
response: options.response,
|
|
66
104
|
}).candidates;
|
|
67
105
|
}
|
|
68
106
|
catch (error) {
|
|
69
107
|
throw new Error(error instanceof Error ? error.message : `${options.providerLabel} returned an invalid response.`);
|
|
70
108
|
}
|
|
71
109
|
}
|
|
110
|
+
function parseProviderJsonResponse(options) {
|
|
111
|
+
return normalizeProviderCandidateResponse({
|
|
112
|
+
response: parseStrictJsonValue(stripMarkdownCodeFence(options.raw), `${options.providerLabel} returned invalid JSON.`),
|
|
113
|
+
request: options.request,
|
|
114
|
+
providerLabel: options.providerLabel,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
72
117
|
function createProviderSignal(signal, timeoutMs) {
|
|
73
|
-
const
|
|
74
|
-
|
|
118
|
+
const controller = new AbortController();
|
|
119
|
+
let didTimeout = false;
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
didTimeout = true;
|
|
122
|
+
controller.abort(new DOMException("The operation was aborted due to timeout", "TimeoutError"));
|
|
123
|
+
}, timeoutMs);
|
|
124
|
+
timer.unref?.();
|
|
125
|
+
const handleAbort = () => {
|
|
126
|
+
controller.abort(signal?.reason);
|
|
127
|
+
};
|
|
128
|
+
if (signal !== undefined) {
|
|
129
|
+
if (signal.aborted) {
|
|
130
|
+
handleAbort();
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
signal.addEventListener("abort", handleAbort, { once: true });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
signal: controller.signal,
|
|
138
|
+
didTimeout() {
|
|
139
|
+
return didTimeout;
|
|
140
|
+
},
|
|
141
|
+
dispose() {
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
signal?.removeEventListener("abort", handleAbort);
|
|
144
|
+
},
|
|
145
|
+
};
|
|
75
146
|
}
|
|
76
147
|
async function requestBuiltInSentences(options) {
|
|
77
148
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
78
|
-
const
|
|
149
|
+
const timeoutMs = Math.min(MINING_BUILTIN_TIMEOUT_MS, options.request.limits.timeoutMs);
|
|
150
|
+
const providerSignal = createProviderSignal(options.signal, timeoutMs);
|
|
79
151
|
try {
|
|
80
152
|
if (options.provider === "openai") {
|
|
81
153
|
const { effectiveModel: model, usingDefaultModel } = resolveBuiltInProviderModel(options.provider, options.modelOverride);
|
|
@@ -98,7 +170,7 @@ async function requestBuiltInSentences(options) {
|
|
|
98
170
|
},
|
|
99
171
|
],
|
|
100
172
|
}),
|
|
101
|
-
signal: providerSignal,
|
|
173
|
+
signal: providerSignal.signal,
|
|
102
174
|
});
|
|
103
175
|
if (response.status === 401 || response.status === 403) {
|
|
104
176
|
throw new MiningProviderRequestError("auth-error", "The built-in OpenAI mining provider rejected the configured API key.");
|
|
@@ -144,8 +216,13 @@ async function requestBuiltInSentences(options) {
|
|
|
144
216
|
content: buildUserPrompt(options.request),
|
|
145
217
|
},
|
|
146
218
|
],
|
|
219
|
+
tools: [ANTHROPIC_MINING_RESPONSE_TOOL],
|
|
220
|
+
tool_choice: {
|
|
221
|
+
type: "tool",
|
|
222
|
+
name: ANTHROPIC_MINING_RESPONSE_TOOL_NAME,
|
|
223
|
+
},
|
|
147
224
|
}),
|
|
148
|
-
signal: providerSignal,
|
|
225
|
+
signal: providerSignal.signal,
|
|
149
226
|
});
|
|
150
227
|
if (response.status === 401 || response.status === 403) {
|
|
151
228
|
throw new MiningProviderRequestError("auth-error", "The built-in Anthropic mining provider rejected the configured API key.");
|
|
@@ -164,8 +241,8 @@ async function requestBuiltInSentences(options) {
|
|
|
164
241
|
throw new MiningProviderRequestError("unavailable", `The built-in Anthropic mining provider returned HTTP ${response.status}.`);
|
|
165
242
|
}
|
|
166
243
|
return annotateProviderCandidates({
|
|
167
|
-
candidates:
|
|
168
|
-
|
|
244
|
+
candidates: normalizeProviderCandidateResponse({
|
|
245
|
+
response: extractAnthropicResponsePayload(await response.json()),
|
|
169
246
|
request: options.request,
|
|
170
247
|
providerLabel: "The built-in Anthropic mining provider",
|
|
171
248
|
}),
|
|
@@ -177,11 +254,21 @@ async function requestBuiltInSentences(options) {
|
|
|
177
254
|
if (error instanceof MiningProviderRequestError) {
|
|
178
255
|
throw error;
|
|
179
256
|
}
|
|
180
|
-
if (
|
|
181
|
-
throw
|
|
257
|
+
if (providerSignal.didTimeout()) {
|
|
258
|
+
throw createBuiltInProviderTimeoutError({
|
|
259
|
+
provider: options.provider,
|
|
260
|
+
timeoutMs,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
if (error instanceof Error
|
|
264
|
+
&& (error.name === "AbortError" || error.name === "TimeoutError")) {
|
|
265
|
+
throw error;
|
|
182
266
|
}
|
|
183
267
|
throw new MiningProviderRequestError("unavailable", error instanceof Error ? error.message : String(error));
|
|
184
268
|
}
|
|
269
|
+
finally {
|
|
270
|
+
providerSignal.dispose();
|
|
271
|
+
}
|
|
185
272
|
}
|
|
186
273
|
function extractOpenAiText(payload) {
|
|
187
274
|
if (payload !== null && typeof payload === "object") {
|
|
@@ -235,6 +322,24 @@ function extractAnthropicText(payload) {
|
|
|
235
322
|
}
|
|
236
323
|
throw new Error("The built-in Anthropic mining provider returned an empty response.");
|
|
237
324
|
}
|
|
325
|
+
function extractAnthropicResponsePayload(payload) {
|
|
326
|
+
if (payload !== null && typeof payload === "object") {
|
|
327
|
+
const content = payload.content;
|
|
328
|
+
if (Array.isArray(content)) {
|
|
329
|
+
for (const entry of content) {
|
|
330
|
+
if (entry === null || typeof entry !== "object") {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const typedEntry = entry;
|
|
334
|
+
if (typedEntry.type === "tool_use"
|
|
335
|
+
&& typedEntry.name === ANTHROPIC_MINING_RESPONSE_TOOL_NAME) {
|
|
336
|
+
return typedEntry.input;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return parseStrictJsonValue(stripMarkdownCodeFence(extractAnthropicText(payload)), "The built-in Anthropic mining provider returned invalid JSON.");
|
|
342
|
+
}
|
|
238
343
|
export async function generateMiningSentences(request, options) {
|
|
239
344
|
const config = await loadClientConfig({
|
|
240
345
|
path: options.paths.clientConfigPath,
|
|
@@ -30,6 +30,7 @@ export interface MiningFollowVisualizerState {
|
|
|
30
30
|
settledBoardEntries: MiningSentenceBoardEntry[];
|
|
31
31
|
provisionalRequiredWords: readonly string[];
|
|
32
32
|
provisionalEntry: MiningProvisionalSentenceEntry;
|
|
33
|
+
provisionalBroadcastTxid: string | null;
|
|
33
34
|
latestSentence: string | null;
|
|
34
35
|
latestTxid: string | null;
|
|
35
36
|
recentWin: MiningRecentWinSummary | null;
|
|
@@ -133,6 +133,16 @@ function formatRequiredWordsLine(words) {
|
|
|
133
133
|
}
|
|
134
134
|
return `Required words: ${words.map((word) => word.toUpperCase()).join(", ")}`;
|
|
135
135
|
}
|
|
136
|
+
function formatProvisionalTxLinkLine(entry, txid) {
|
|
137
|
+
if (entry.domainName === null || entry.sentence === null || txid === null) {
|
|
138
|
+
return "";
|
|
139
|
+
}
|
|
140
|
+
const normalizedTxid = normalizeInlineText(txid);
|
|
141
|
+
if (normalizedTxid.length === 0) {
|
|
142
|
+
return "";
|
|
143
|
+
}
|
|
144
|
+
return `View at: https://mempool.space/tx/${normalizedTxid}`;
|
|
145
|
+
}
|
|
136
146
|
function formatProvisionalSentenceRow(entry, requiredWords) {
|
|
137
147
|
if (entry.domainName === null || entry.sentence === null) {
|
|
138
148
|
return ["", "", ""];
|
|
@@ -151,6 +161,7 @@ export function createEmptyMiningFollowVisualizerState() {
|
|
|
151
161
|
domainName: null,
|
|
152
162
|
sentence: null,
|
|
153
163
|
},
|
|
164
|
+
provisionalBroadcastTxid: null,
|
|
154
165
|
latestSentence: null,
|
|
155
166
|
latestTxid: null,
|
|
156
167
|
recentWin: null,
|
|
@@ -173,6 +184,7 @@ function cloneMiningFollowVisualizerState(state) {
|
|
|
173
184
|
provisionalEntry: {
|
|
174
185
|
...state.provisionalEntry,
|
|
175
186
|
},
|
|
187
|
+
provisionalBroadcastTxid: state.provisionalBroadcastTxid,
|
|
176
188
|
recentWin: state.recentWin === null
|
|
177
189
|
? null
|
|
178
190
|
: {
|
|
@@ -375,6 +387,7 @@ export class MiningFollowVisualizer {
|
|
|
375
387
|
: formatSentenceRow(entry);
|
|
376
388
|
}).flat(),
|
|
377
389
|
"----------",
|
|
390
|
+
formatProvisionalTxLinkLine(uiState.provisionalEntry, uiState.provisionalBroadcastTxid),
|
|
378
391
|
formatRequiredWordsLine(uiState.provisionalRequiredWords),
|
|
379
392
|
...formatProvisionalSentenceRow(uiState.provisionalEntry, uiState.provisionalRequiredWords),
|
|
380
393
|
],
|
package/package.json
CHANGED