@genkaku/dsh-user-plugin 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/dist/adapter.d.ts +44 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +226 -0
- package/dist/adapter.test.d.ts +2 -0
- package/dist/adapter.test.d.ts.map +1 -0
- package/dist/adapter.test.js +29 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/package.json +61 -3
- package/README.md +0 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GenKaKu Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenKaKu as a first-class DeepSeek Harness model provider.
|
|
3
|
+
*
|
|
4
|
+
* The harness asks an adapter for StreamChunks in its own vocabulary; the
|
|
5
|
+
* network speaks OpenAI chat-completions over the plugin's local bridge. This
|
|
6
|
+
* adapter is the whole translation and the only place that knows both dialects.
|
|
7
|
+
*
|
|
8
|
+
* All traffic goes through the bridge (127.0.0.1:<port>/v1) instead of dialing a
|
|
9
|
+
* worker directly, so escrow opening, per-minute billing and settlement stay in
|
|
10
|
+
* one implementation.
|
|
11
|
+
*/
|
|
12
|
+
import type { FinishReason, GenerateOptions, LlmModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
13
|
+
import { LlmAdapter } from '@deepseek-ai/dsh-llm';
|
|
14
|
+
export type GenkakuAdapterOptions = {
|
|
15
|
+
/** Base URL of the plugin's local bridge, e.g. http://127.0.0.1:47821/v1 */
|
|
16
|
+
baseUrl: () => string;
|
|
17
|
+
log?: (line: string) => void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Project harness messages onto the OpenAI chat shape the bridge accepts.
|
|
21
|
+
*
|
|
22
|
+
* `developer` messages carry tool additions and removals, which the loop already
|
|
23
|
+
* expresses through GenerateOptions.tools; they are skipped rather than sent as
|
|
24
|
+
* empty turns.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toOpenAiMessages(options: GenerateOptions): Array<Record<string, unknown>>;
|
|
27
|
+
export declare function finishOf(reason: string | null | undefined, sawToolCall: boolean): FinishReason;
|
|
28
|
+
export declare class GenkakuAdapter extends LlmAdapter {
|
|
29
|
+
private readonly options;
|
|
30
|
+
constructor(options: GenkakuAdapterOptions);
|
|
31
|
+
providerInfo(provider: string): {
|
|
32
|
+
id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The live marketplace board. A chat-only model is still listed: it is usable,
|
|
37
|
+
* it just cannot drive an agent loop, so it carries a warning badge instead of
|
|
38
|
+
* being hidden.
|
|
39
|
+
*/
|
|
40
|
+
listModels(provider: string): Promise<readonly LlmModelInfo[]>;
|
|
41
|
+
/** Stream one completion from the network. */
|
|
42
|
+
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAEV,YAAY,EACZ,eAAe,EACf,YAAY,EAEZ,WAAW,EAGZ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAqBjD,MAAM,MAAM,qBAAqB,GAAG;IAClC,4EAA4E;IAC5E,OAAO,EAAE,MAAM,MAAM,CAAA;IACrB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7B,CAAA;AAgBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAqBzF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,WAAW,EAAE,OAAO,GAAG,YAAY,CAI9F;AAED,qBAAa,cAAe,SAAQ,UAAU;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,qBAAqB;IAIlD,YAAY,CAAC,QAAQ,EAAE,MAAM;;;;IAItC;;;;OAIG;IACY,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC;IAyB7E,8CAA8C;IAC9B,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,aAAa,CAAC,WAAW,CAAC;CAuH7E"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenKaKu as a first-class DeepSeek Harness model provider.
|
|
3
|
+
*
|
|
4
|
+
* The harness asks an adapter for StreamChunks in its own vocabulary; the
|
|
5
|
+
* network speaks OpenAI chat-completions over the plugin's local bridge. This
|
|
6
|
+
* adapter is the whole translation and the only place that knows both dialects.
|
|
7
|
+
*
|
|
8
|
+
* All traffic goes through the bridge (127.0.0.1:<port>/v1) instead of dialing a
|
|
9
|
+
* worker directly, so escrow opening, per-minute billing and settlement stay in
|
|
10
|
+
* one implementation.
|
|
11
|
+
*/
|
|
12
|
+
import { LlmAdapter } from '@deepseek-ai/dsh-llm';
|
|
13
|
+
function textOf(blocks) {
|
|
14
|
+
return blocks.map(block => (block.type === 'text' ? block.text : '')).join('');
|
|
15
|
+
}
|
|
16
|
+
function toolCallsOf(blocks) {
|
|
17
|
+
return blocks
|
|
18
|
+
.filter((block) => block.type === 'tool-call')
|
|
19
|
+
.map(block => ({
|
|
20
|
+
id: block.id,
|
|
21
|
+
type: 'function',
|
|
22
|
+
function: { name: block.name, arguments: block.arguments },
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Project harness messages onto the OpenAI chat shape the bridge accepts.
|
|
27
|
+
*
|
|
28
|
+
* `developer` messages carry tool additions and removals, which the loop already
|
|
29
|
+
* expresses through GenerateOptions.tools; they are skipped rather than sent as
|
|
30
|
+
* empty turns.
|
|
31
|
+
*/
|
|
32
|
+
export function toOpenAiMessages(options) {
|
|
33
|
+
const out = [];
|
|
34
|
+
if (options.system != null && options.system !== '')
|
|
35
|
+
out.push({ role: 'system', content: options.system });
|
|
36
|
+
for (const message of options.messages) {
|
|
37
|
+
if (message.role === 'developer')
|
|
38
|
+
continue;
|
|
39
|
+
if (message.role === 'tool') {
|
|
40
|
+
out.push({ role: 'tool', tool_call_id: message.toolCallId, content: textOf(message.content) });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (message.role === 'assistant') {
|
|
44
|
+
const calls = toolCallsOf(message.content);
|
|
45
|
+
out.push({
|
|
46
|
+
role: 'assistant',
|
|
47
|
+
content: textOf(message.content),
|
|
48
|
+
...(calls.length === 0 ? {} : { tool_calls: calls }),
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
out.push({ role: message.role, content: textOf(message.content) });
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
export function finishOf(reason, sawToolCall) {
|
|
57
|
+
if (sawToolCall || reason === 'tool_calls')
|
|
58
|
+
return { kind: 'tool-calls' };
|
|
59
|
+
if (reason === 'length')
|
|
60
|
+
return { kind: 'max-tokens' };
|
|
61
|
+
return { kind: 'stop' };
|
|
62
|
+
}
|
|
63
|
+
export class GenkakuAdapter extends LlmAdapter {
|
|
64
|
+
options;
|
|
65
|
+
constructor(options) {
|
|
66
|
+
super();
|
|
67
|
+
this.options = options;
|
|
68
|
+
}
|
|
69
|
+
providerInfo(provider) {
|
|
70
|
+
return { id: provider, name: 'GenKaKu Network' };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The live marketplace board. A chat-only model is still listed: it is usable,
|
|
74
|
+
* it just cannot drive an agent loop, so it carries a warning badge instead of
|
|
75
|
+
* being hidden.
|
|
76
|
+
*/
|
|
77
|
+
async listModels(provider) {
|
|
78
|
+
try {
|
|
79
|
+
const res = await fetch(`${this.options.baseUrl()}/models`);
|
|
80
|
+
if (!res.ok)
|
|
81
|
+
return [];
|
|
82
|
+
const body = (await res.json());
|
|
83
|
+
return (body.data ?? []).map((model) => {
|
|
84
|
+
const tier = model.genkaku?.tier ?? 'chat';
|
|
85
|
+
const size = model.genkaku?.parameterSize;
|
|
86
|
+
const bits = [size, tier === 'agent' ? 'agent-ready' : undefined].filter(Boolean);
|
|
87
|
+
return {
|
|
88
|
+
provider,
|
|
89
|
+
id: model.id,
|
|
90
|
+
name: model.id,
|
|
91
|
+
...(bits.length === 0 ? {} : { description: bits.join(' · ') }),
|
|
92
|
+
...(tier === 'chat'
|
|
93
|
+
? { badge: { text: { en: 'not for agents', zh: '不適用於 agent' }, tone: 'warning' } }
|
|
94
|
+
: {}),
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
this.options.log?.(`genkaku adapter: model listing failed (${String(error)})`);
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Stream one completion from the network. */
|
|
104
|
+
async *stream(options) {
|
|
105
|
+
const body = {
|
|
106
|
+
model: options.model,
|
|
107
|
+
stream: true,
|
|
108
|
+
messages: toOpenAiMessages(options),
|
|
109
|
+
...(options.maxTokens === undefined ? {} : { max_tokens: options.maxTokens }),
|
|
110
|
+
...(options.temperature === undefined ? {} : { temperature: options.temperature }),
|
|
111
|
+
...(options.tools === undefined || options.tools.length === 0
|
|
112
|
+
? {}
|
|
113
|
+
: {
|
|
114
|
+
tools: options.tools.map(tool => ({
|
|
115
|
+
type: 'function',
|
|
116
|
+
function: { name: tool.name, description: tool.description, parameters: tool.parameters },
|
|
117
|
+
})),
|
|
118
|
+
}),
|
|
119
|
+
};
|
|
120
|
+
const res = await fetch(`${this.options.baseUrl()}/chat/completions`, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: { 'content-type': 'application/json' },
|
|
123
|
+
body: JSON.stringify(body),
|
|
124
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
125
|
+
});
|
|
126
|
+
if (!res.ok || res.body == null) {
|
|
127
|
+
const detail = await res.text().catch(() => '');
|
|
128
|
+
throw new Error(`genkaku: bridge refused (${res.status}) ${detail.slice(0, 200)}`);
|
|
129
|
+
}
|
|
130
|
+
const pending = [];
|
|
131
|
+
let nextIndex = 0;
|
|
132
|
+
let textIndex = null;
|
|
133
|
+
let text = '';
|
|
134
|
+
const tools = new Map();
|
|
135
|
+
let usage = null;
|
|
136
|
+
let sawToolCall = false;
|
|
137
|
+
const closeText = () => {
|
|
138
|
+
if (textIndex == null)
|
|
139
|
+
return;
|
|
140
|
+
pending.push({ type: 'block-end', index: textIndex, block: { type: 'text', text } });
|
|
141
|
+
textIndex = null;
|
|
142
|
+
text = '';
|
|
143
|
+
};
|
|
144
|
+
const handle = (raw) => {
|
|
145
|
+
let chunk;
|
|
146
|
+
try {
|
|
147
|
+
chunk = JSON.parse(raw);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const choice = chunk.choices?.[0];
|
|
153
|
+
const content = choice?.delta?.content;
|
|
154
|
+
if (typeof content === 'string' && content !== '') {
|
|
155
|
+
if (textIndex == null) {
|
|
156
|
+
textIndex = nextIndex++;
|
|
157
|
+
pending.push({ type: 'block-start', index: textIndex, blockType: 'text' });
|
|
158
|
+
}
|
|
159
|
+
text += content;
|
|
160
|
+
pending.push({ type: 'text-delta', index: textIndex, text: content });
|
|
161
|
+
}
|
|
162
|
+
for (const call of choice?.delta?.tool_calls ?? []) {
|
|
163
|
+
const slot = call.index ?? 0;
|
|
164
|
+
let entry = tools.get(slot);
|
|
165
|
+
if (entry == null) {
|
|
166
|
+
sawToolCall = true;
|
|
167
|
+
closeText();
|
|
168
|
+
entry = { index: nextIndex++, id: (call.id ?? `call_${slot}`), name: call.function?.name ?? '', args: '' };
|
|
169
|
+
tools.set(slot, entry);
|
|
170
|
+
pending.push({ type: 'block-start', index: entry.index, blockType: 'tool-call' });
|
|
171
|
+
pending.push({ type: 'tool-call-delta', index: entry.index, id: entry.id, name: entry.name, argumentsDelta: '' });
|
|
172
|
+
}
|
|
173
|
+
const argsDelta = call.function?.arguments ?? '';
|
|
174
|
+
if (argsDelta !== '') {
|
|
175
|
+
entry.args += argsDelta;
|
|
176
|
+
pending.push({ type: 'tool-call-delta', index: entry.index, id: entry.id, argumentsDelta: argsDelta });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (chunk.usage != null) {
|
|
180
|
+
usage = {
|
|
181
|
+
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
182
|
+
outputTokens: chunk.usage.completion_tokens ?? 0,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const reader = res.body.getReader();
|
|
187
|
+
const decoder = new TextDecoder();
|
|
188
|
+
let buffer = '';
|
|
189
|
+
for (;;) {
|
|
190
|
+
const { done, value } = await reader.read();
|
|
191
|
+
if (done)
|
|
192
|
+
break;
|
|
193
|
+
buffer += decoder.decode(value, { stream: true });
|
|
194
|
+
let nl = buffer.indexOf('\n');
|
|
195
|
+
while (nl >= 0) {
|
|
196
|
+
const line = buffer.slice(0, nl).trim();
|
|
197
|
+
buffer = buffer.slice(nl + 1);
|
|
198
|
+
nl = buffer.indexOf('\n');
|
|
199
|
+
if (!line.startsWith('data:')) {
|
|
200
|
+
for (const chunk of pending.splice(0))
|
|
201
|
+
yield chunk;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const payload = line.slice(5).trim();
|
|
205
|
+
if (payload !== "")
|
|
206
|
+
handle(payload);
|
|
207
|
+
for (const chunk of pending.splice(0))
|
|
208
|
+
yield chunk;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
closeText();
|
|
212
|
+
for (const chunk of pending.splice(0))
|
|
213
|
+
yield chunk;
|
|
214
|
+
for (const entry of [...tools.values()].sort((a, b) => a.index - b.index)) {
|
|
215
|
+
yield {
|
|
216
|
+
type: 'block-end',
|
|
217
|
+
index: entry.index,
|
|
218
|
+
block: { type: 'tool-call', id: entry.id, name: entry.name, arguments: entry.args },
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
if (usage != null)
|
|
222
|
+
yield { type: 'usage', usage };
|
|
223
|
+
yield { type: 'finish', reason: finishOf(undefined, sawToolCall) };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.test.d.ts","sourceRoot":"","sources":["../src/adapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { test } from 'node:test';
|
|
3
|
+
import { finishOf, GenkakuAdapter, toOpenAiMessages } from "./adapter.js";
|
|
4
|
+
test('developer messages are dropped and tool results keep their call id', () => {
|
|
5
|
+
const options = {
|
|
6
|
+
provider: 'genkaku',
|
|
7
|
+
model: 'm',
|
|
8
|
+
messages: [
|
|
9
|
+
{ role: 'developer', content: [] },
|
|
10
|
+
{ role: 'user', content: [{ type: 'text', text: 'hi' }] },
|
|
11
|
+
{ role: 'assistant', content: [{ type: 'tool-call', id: 'c1', name: 'read', arguments: '{}' }] },
|
|
12
|
+
{ role: 'tool', toolCallId: 'c1', content: [{ type: 'text', text: 'ok' }] },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
const out = toOpenAiMessages(options);
|
|
16
|
+
assert.equal(out.length, 3);
|
|
17
|
+
assert.deepEqual(out[1], {
|
|
18
|
+
role: 'assistant',
|
|
19
|
+
content: '',
|
|
20
|
+
tool_calls: [{ id: 'c1', type: 'function', function: { name: 'read', arguments: '{}' } }],
|
|
21
|
+
});
|
|
22
|
+
assert.deepEqual(out[2], { role: 'tool', tool_call_id: 'c1', content: 'ok' });
|
|
23
|
+
});
|
|
24
|
+
test('a tool call finishes as tool-calls, not stop', () => {
|
|
25
|
+
assert.deepEqual(finishOf(null, true), { kind: 'tool-calls' });
|
|
26
|
+
assert.deepEqual(finishOf('length', false), { kind: 'max-tokens' });
|
|
27
|
+
assert.deepEqual(finishOf('stop', false), { kind: 'stop' });
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=adapter.test.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenKaKu user plugin — buy inference from the network.
|
|
3
|
+
*
|
|
4
|
+
* Pinned to the consuming role. It opens escrow sessions, streams tokens back
|
|
5
|
+
* over libp2p, and settles per started minute.
|
|
6
|
+
*
|
|
7
|
+
* The other half of this package is the harness integration: a GenKaKu model
|
|
8
|
+
* provider whose route is the plugin's own local bridge. Registering it here
|
|
9
|
+
* rather than asking the user to hand-write a pi-ai route is what makes the
|
|
10
|
+
* network appear in the normal model selector, with a warning badge on models
|
|
11
|
+
* that cannot drive an agent loop.
|
|
12
|
+
*/
|
|
13
|
+
/** Provider route key; also the prefix users see in model lists. */
|
|
14
|
+
export declare const PROVIDER = "genkaku";
|
|
15
|
+
export declare const name: string, inject: readonly string[], Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
|
|
16
|
+
mode: import("@deepseek-ai/schemastery").default<"user" | "localPlusSharing" | "workerDedicated", "user" | "localPlusSharing" | "workerDedicated", "volatile-defined">;
|
|
17
|
+
env: import("@deepseek-ai/schemastery").default<"base-sepolia" | "base", "base-sepolia" | "base", "defined">;
|
|
18
|
+
port: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
19
|
+
workers: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
20
|
+
settle: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
21
|
+
paywall: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
22
|
+
keyDir: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
23
|
+
catalog: import("@deepseek-ai/schemastery").default<({
|
|
24
|
+
model?: string | null | undefined;
|
|
25
|
+
pricePerHour?: string | null | undefined;
|
|
26
|
+
city?: string | null | undefined;
|
|
27
|
+
creditScore?: number | null | undefined;
|
|
28
|
+
sessionsServed?: number | null | undefined;
|
|
29
|
+
maxConcurrent?: number | null | undefined;
|
|
30
|
+
} & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<NoInfer<{
|
|
31
|
+
model: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
32
|
+
pricePerHour: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
33
|
+
city: import("@deepseek-ai/schemastery").default<string, string, "plain">;
|
|
34
|
+
creditScore: import("@deepseek-ai/schemastery").default<number, number, "defined">;
|
|
35
|
+
sessionsServed: import("@deepseek-ai/schemastery").default<number, number, "defined">;
|
|
36
|
+
maxConcurrent: import("@deepseek-ai/schemastery").default<number, number, "plain">;
|
|
37
|
+
}>>[], "defined">;
|
|
38
|
+
listen: import("@deepseek-ai/schemastery").default<string[], string[], "defined">;
|
|
39
|
+
publishAddress: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
40
|
+
workerName: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
41
|
+
spawnOllama: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
42
|
+
ollamaHost: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
43
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
44
|
+
mode: import("@deepseek-ai/schemastery").default<"user" | "localPlusSharing" | "workerDedicated", "user" | "localPlusSharing" | "workerDedicated", "volatile-defined">;
|
|
45
|
+
env: import("@deepseek-ai/schemastery").default<"base-sepolia" | "base", "base-sepolia" | "base", "defined">;
|
|
46
|
+
port: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
47
|
+
workers: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
48
|
+
settle: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
49
|
+
paywall: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
50
|
+
keyDir: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
51
|
+
catalog: import("@deepseek-ai/schemastery").default<({
|
|
52
|
+
model?: string | null | undefined;
|
|
53
|
+
pricePerHour?: string | null | undefined;
|
|
54
|
+
city?: string | null | undefined;
|
|
55
|
+
creditScore?: number | null | undefined;
|
|
56
|
+
sessionsServed?: number | null | undefined;
|
|
57
|
+
maxConcurrent?: number | null | undefined;
|
|
58
|
+
} & import("@deepseek-ai/cosmokit").Dict)[], Schemastery.ObjectT<NoInfer<{
|
|
59
|
+
model: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
60
|
+
pricePerHour: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
61
|
+
city: import("@deepseek-ai/schemastery").default<string, string, "plain">;
|
|
62
|
+
creditScore: import("@deepseek-ai/schemastery").default<number, number, "defined">;
|
|
63
|
+
sessionsServed: import("@deepseek-ai/schemastery").default<number, number, "defined">;
|
|
64
|
+
maxConcurrent: import("@deepseek-ai/schemastery").default<number, number, "plain">;
|
|
65
|
+
}>>[], "defined">;
|
|
66
|
+
listen: import("@deepseek-ai/schemastery").default<string[], string[], "defined">;
|
|
67
|
+
publishAddress: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
68
|
+
workerName: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
69
|
+
spawnOllama: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
|
|
70
|
+
ollamaHost: import("@deepseek-ai/schemastery").default<string, string, "defined">;
|
|
71
|
+
}>>, "plain">, apply: (ctx: import("@deepseek-ai/cordis").Context, config: import("@genkaku/dsh-runtime").Config) => Promise<void>;
|
|
72
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,oEAAoE;AACpE,eAAO,MAAM,QAAQ,YAAY,CAAA;AAEjC,eAAO,MAAQ,IAAI,UAAE,MAAM,qBAAE,MAAM;;;;;;;;;aAyB4pL,CAAC;oBAAqD,CAAC;YAA6C,CAAC;mBAAoD,CAAC;sBAAuD,CAAC;qBAAsD,CAAC;;;;;;;;;;;;;;;;;;;;;;;aAAiwC,CAAC;oBAAqD,CAAC;YAA6C,CAAC;mBAAoD,CAAC;sBAAuD,CAAC;qBAAsD,CAAC;;;;;;;;;;;;;;eAzB76O,KAAK,8GAwBxC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenKaKu user plugin — buy inference from the network.
|
|
3
|
+
*
|
|
4
|
+
* Pinned to the consuming role. It opens escrow sessions, streams tokens back
|
|
5
|
+
* over libp2p, and settles per started minute.
|
|
6
|
+
*
|
|
7
|
+
* The other half of this package is the harness integration: a GenKaKu model
|
|
8
|
+
* provider whose route is the plugin's own local bridge. Registering it here
|
|
9
|
+
* rather than asking the user to hand-write a pi-ai route is what makes the
|
|
10
|
+
* network appear in the normal model selector, with a warning badge on models
|
|
11
|
+
* that cannot drive an agent loop.
|
|
12
|
+
*/
|
|
13
|
+
import { defineGenkakuPlugin } from '@genkaku/dsh-runtime';
|
|
14
|
+
import { GenkakuAdapter } from "./adapter.js";
|
|
15
|
+
/** Provider route key; also the prefix users see in model lists. */
|
|
16
|
+
export const PROVIDER = 'genkaku';
|
|
17
|
+
export const { name, inject, Config, apply } = defineGenkakuPlugin({
|
|
18
|
+
pluginName: 'genkaku-user',
|
|
19
|
+
settingsNs: 'genkaku-user',
|
|
20
|
+
role: 'user',
|
|
21
|
+
injectExtra: ['llm'],
|
|
22
|
+
onStarted: (ctx, config) => {
|
|
23
|
+
const log = ctx.logger('genkaku-user');
|
|
24
|
+
const baseUrl = () => `http://127.0.0.1:${config.port.get()}/v1`;
|
|
25
|
+
const adapter = new GenkakuAdapter({ baseUrl, log: line => log.info(line) });
|
|
26
|
+
ctx.effect(() => ctx.llm.registerAdapter([PROVIDER], adapter), 'genkaku-user: llm adapter');
|
|
27
|
+
ctx.effect(() => ctx.llm.registerConfigurableProviders([{
|
|
28
|
+
provider: PROVIDER,
|
|
29
|
+
displayName: 'GenKaKu Network',
|
|
30
|
+
settingsNs: 'genkaku-user',
|
|
31
|
+
settingsPath: [],
|
|
32
|
+
}]), 'genkaku-user: provider route');
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genkaku/dsh-user-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Buy peer-to-peer inference from the GenKaKu network",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist/**/*.js",
|
|
18
|
+
"dist/**/*.d.ts",
|
|
19
|
+
"dist/**/*.d.ts.map",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"dsh": {
|
|
24
|
+
"target": ">=0.2.2-rc.2"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@deepseek-ai/dsh-llm": "0.2.2-rc.1",
|
|
28
|
+
"@genkaku/protocol": "0.1.1",
|
|
29
|
+
"@genkaku/dsh-runtime": "0.1.1"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@deepseek-ai/cordis": "^4.1.0",
|
|
33
|
+
"@deepseek-ai/dsh-llm": "0.2.2-rc.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@deepseek-ai/cordis": "^4.1.0",
|
|
37
|
+
"@deepseek-ai/dsh-settings": "0.2.2-rc.1"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/mlaimail77/genkaku-plugin.git",
|
|
42
|
+
"directory": "packages/user"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"author": "GenKaKu Labs",
|
|
48
|
+
"homepage": "https://staging.genkaku.app",
|
|
49
|
+
"keywords": [
|
|
50
|
+
"deepseek-harness",
|
|
51
|
+
"dsh",
|
|
52
|
+
"dsh-plugin",
|
|
53
|
+
"p2p",
|
|
54
|
+
"libp2p",
|
|
55
|
+
"ollama",
|
|
56
|
+
"usdc",
|
|
57
|
+
"base"
|
|
58
|
+
],
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc -p tsconfig.json",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
62
|
+
"test": "tsc -p tsconfig.json && node --test dist/*.test.js"
|
|
63
|
+
}
|
|
6
64
|
}
|
package/README.md
DELETED