@antseed/provider-local-llm 0.1.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/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # @antseed/provider-local-llm
2
+
3
+ Sell local LLM capacity on the Antseed P2P network. Works with Ollama, llama.cpp, and any OpenAI-compatible local server.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ antseed plugin add @antseed/provider-local-llm
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ # With Ollama (default)
15
+ antseed seed --provider local-llm
16
+
17
+ # With a custom endpoint
18
+ export LOCAL_LLM_BASE_URL=http://localhost:8080
19
+ antseed seed --provider local-llm
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ | Key | Type | Required | Default | Description |
25
+ |-----|------|----------|---------|-------------|
26
+ | `LOCAL_LLM_BASE_URL` | string | No | `http://localhost:11434` | Local LLM server URL |
27
+ | `LOCAL_LLM_API_KEY` | secret | No | -- | Optional API key for local server |
28
+ | `ANTSEED_INPUT_USD_PER_MILLION` | number | No | 0 | Input token price (USD per 1M) |
29
+ | `ANTSEED_OUTPUT_USD_PER_MILLION` | number | No | 0 | Output token price (USD per 1M) |
30
+ | `ANTSEED_MAX_CONCURRENCY` | number | No | 1 | Max concurrent requests |
31
+ | `ANTSEED_ALLOWED_MODELS` | string[] | No | -- | Comma-separated model allowlist |
32
+
33
+ ## How It Works
34
+
35
+ Relays requests to a local LLM server. Pricing defaults to 0 (free) since you're running your own hardware. Concurrency defaults to 1 to avoid overloading local inference.
@@ -0,0 +1,4 @@
1
+ import type { AntseedProviderPlugin } from '@antseed/node';
2
+ declare const plugin: AntseedProviderPlugin;
3
+ export default plugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAY,MAAM,eAAe,CAAC;AAWrE,QAAA,MAAM,MAAM,EAAE,qBAmDb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,56 @@
1
+ import { BaseProvider, StaticTokenProvider } from '@antseed/provider-core';
2
+ function parseNonNegativeNumber(raw, key, fallback) {
3
+ const parsed = raw === undefined ? fallback : Number.parseFloat(raw);
4
+ if (!Number.isFinite(parsed) || parsed < 0) {
5
+ throw new Error(`${key} must be a non-negative number`);
6
+ }
7
+ return parsed;
8
+ }
9
+ const plugin = {
10
+ name: 'local-llm',
11
+ displayName: 'Local LLM',
12
+ version: '0.1.0',
13
+ type: 'provider',
14
+ description: 'Sell local LLM capacity to P2P peers',
15
+ configSchema: [
16
+ { key: 'LOCAL_LLM_BASE_URL', label: 'Base URL', type: 'string', required: false, default: 'http://localhost:11434', description: 'Local LLM server base URL' },
17
+ { key: 'LOCAL_LLM_API_KEY', label: 'API Key', type: 'secret', required: false, description: 'Optional API key for local LLM' },
18
+ { key: 'ANTSEED_INPUT_USD_PER_MILLION', label: 'Input Price', type: 'number', required: false, default: 0, description: 'Input price in USD per 1M tokens' },
19
+ { key: 'ANTSEED_OUTPUT_USD_PER_MILLION', label: 'Output Price', type: 'number', required: false, default: 0, description: 'Output price in USD per 1M tokens' },
20
+ { key: 'ANTSEED_MAX_CONCURRENCY', label: 'Max Concurrency', type: 'number', required: false, default: 1, description: 'Max concurrent requests' },
21
+ { key: 'ANTSEED_ALLOWED_MODELS', label: 'Allowed Models', type: 'string[]', required: false, description: 'Model allow-list' },
22
+ ],
23
+ createProvider(config) {
24
+ const baseUrl = config['LOCAL_LLM_BASE_URL'] ?? 'http://localhost:11434';
25
+ const apiKey = config['LOCAL_LLM_API_KEY'] ?? '';
26
+ const pricing = {
27
+ defaults: {
28
+ inputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_INPUT_USD_PER_MILLION'], 'ANTSEED_INPUT_USD_PER_MILLION', 0),
29
+ outputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_OUTPUT_USD_PER_MILLION'], 'ANTSEED_OUTPUT_USD_PER_MILLION', 0),
30
+ },
31
+ };
32
+ const maxConcurrency = parseInt(config['ANTSEED_MAX_CONCURRENCY'] ?? '1', 10);
33
+ if (Number.isNaN(maxConcurrency)) {
34
+ throw new Error('ANTSEED_MAX_CONCURRENCY must be a valid number');
35
+ }
36
+ const allowedModels = config['ANTSEED_ALLOWED_MODELS']
37
+ ? config['ANTSEED_ALLOWED_MODELS'].split(',').map((s) => s.trim())
38
+ : [];
39
+ const tokenProvider = apiKey ? new StaticTokenProvider(apiKey) : undefined;
40
+ return new BaseProvider({
41
+ name: 'local-llm',
42
+ models: allowedModels,
43
+ pricing,
44
+ relay: {
45
+ baseUrl,
46
+ authHeaderName: 'authorization',
47
+ authHeaderValue: apiKey ? `Bearer ${apiKey}` : '',
48
+ tokenProvider,
49
+ maxConcurrency,
50
+ allowedModels,
51
+ },
52
+ });
53
+ },
54
+ };
55
+ export default plugin;
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@antseed/provider-local-llm",
3
+ "version": "0.1.0",
4
+ "description": "Local LLM provider plugin for Antseed",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "prebuild": "rm -rf dist",
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "typecheck": "tsc --noEmit"
13
+ },
14
+ "dependencies": {
15
+ "@antseed/provider-core": "workspace:*"
16
+ },
17
+ "peerDependencies": {
18
+ "@antseed/node": ">=0.1.0"
19
+ },
20
+ "devDependencies": {
21
+ "typescript": "^5.5.0",
22
+ "vitest": "^2.0.0",
23
+ "@antseed/node": "workspace:*"
24
+ }
25
+ }
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import plugin from './index.js';
3
+
4
+ describe('provider-local-llm plugin', () => {
5
+ it('has correct name and metadata', () => {
6
+ expect(plugin.name).toBe('local-llm');
7
+ expect(plugin.displayName).toBe('Local LLM');
8
+ expect(plugin.type).toBe('provider');
9
+ expect(plugin.version).toBe('0.1.0');
10
+ });
11
+
12
+ it('has configSchema with expected fields', () => {
13
+ const keys = plugin.configSchema!.map((f) => f.key);
14
+ expect(keys).toContain('LOCAL_LLM_BASE_URL');
15
+ expect(keys).toContain('LOCAL_LLM_API_KEY');
16
+ expect(keys).toContain('ANTSEED_INPUT_USD_PER_MILLION');
17
+ expect(keys).toContain('ANTSEED_OUTPUT_USD_PER_MILLION');
18
+ expect(keys).toContain('ANTSEED_MAX_CONCURRENCY');
19
+ expect(keys).toContain('ANTSEED_ALLOWED_MODELS');
20
+ });
21
+
22
+ it('creates provider with default config', () => {
23
+ const provider = plugin.createProvider({});
24
+ expect(provider.name).toBe('local-llm');
25
+ expect(provider.pricing.defaults.inputUsdPerMillion).toBe(0);
26
+ expect(provider.pricing.defaults.outputUsdPerMillion).toBe(0);
27
+ expect(provider.maxConcurrency).toBe(1);
28
+ });
29
+
30
+ it('creates provider with custom base URL and API key', () => {
31
+ const provider = plugin.createProvider({
32
+ LOCAL_LLM_BASE_URL: 'http://192.168.1.100:8080',
33
+ LOCAL_LLM_API_KEY: 'my-local-key',
34
+ });
35
+ expect(provider.name).toBe('local-llm');
36
+ expect(provider.maxConcurrency).toBe(1);
37
+ });
38
+
39
+ it('applies custom concurrency', () => {
40
+ const provider = plugin.createProvider({
41
+ ANTSEED_MAX_CONCURRENCY: '4',
42
+ });
43
+ expect(provider.maxConcurrency).toBe(4);
44
+ });
45
+ });
package/src/index.ts ADDED
@@ -0,0 +1,65 @@
1
+ import type { AntseedProviderPlugin, Provider } from '@antseed/node';
2
+ import { BaseProvider, StaticTokenProvider } from '@antseed/provider-core';
3
+
4
+ function parseNonNegativeNumber(raw: string | undefined, key: string, fallback: number): number {
5
+ const parsed = raw === undefined ? fallback : Number.parseFloat(raw);
6
+ if (!Number.isFinite(parsed) || parsed < 0) {
7
+ throw new Error(`${key} must be a non-negative number`);
8
+ }
9
+ return parsed;
10
+ }
11
+
12
+ const plugin: AntseedProviderPlugin = {
13
+ name: 'local-llm',
14
+ displayName: 'Local LLM',
15
+ version: '0.1.0',
16
+ type: 'provider',
17
+ description: 'Sell local LLM capacity to P2P peers',
18
+ configSchema: [
19
+ { key: 'LOCAL_LLM_BASE_URL', label: 'Base URL', type: 'string', required: false, default: 'http://localhost:11434', description: 'Local LLM server base URL' },
20
+ { key: 'LOCAL_LLM_API_KEY', label: 'API Key', type: 'secret', required: false, description: 'Optional API key for local LLM' },
21
+ { key: 'ANTSEED_INPUT_USD_PER_MILLION', label: 'Input Price', type: 'number', required: false, default: 0, description: 'Input price in USD per 1M tokens' },
22
+ { key: 'ANTSEED_OUTPUT_USD_PER_MILLION', label: 'Output Price', type: 'number', required: false, default: 0, description: 'Output price in USD per 1M tokens' },
23
+ { key: 'ANTSEED_MAX_CONCURRENCY', label: 'Max Concurrency', type: 'number', required: false, default: 1, description: 'Max concurrent requests' },
24
+ { key: 'ANTSEED_ALLOWED_MODELS', label: 'Allowed Models', type: 'string[]', required: false, description: 'Model allow-list' },
25
+ ],
26
+
27
+ createProvider(config: Record<string, string>): Provider {
28
+ const baseUrl = config['LOCAL_LLM_BASE_URL'] ?? 'http://localhost:11434';
29
+ const apiKey = config['LOCAL_LLM_API_KEY'] ?? '';
30
+
31
+ const pricing: Provider['pricing'] = {
32
+ defaults: {
33
+ inputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_INPUT_USD_PER_MILLION'], 'ANTSEED_INPUT_USD_PER_MILLION', 0),
34
+ outputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_OUTPUT_USD_PER_MILLION'], 'ANTSEED_OUTPUT_USD_PER_MILLION', 0),
35
+ },
36
+ };
37
+
38
+ const maxConcurrency = parseInt(config['ANTSEED_MAX_CONCURRENCY'] ?? '1', 10);
39
+ if (Number.isNaN(maxConcurrency)) {
40
+ throw new Error('ANTSEED_MAX_CONCURRENCY must be a valid number');
41
+ }
42
+
43
+ const allowedModels = config['ANTSEED_ALLOWED_MODELS']
44
+ ? config['ANTSEED_ALLOWED_MODELS'].split(',').map((s: string) => s.trim())
45
+ : [];
46
+
47
+ const tokenProvider = apiKey ? new StaticTokenProvider(apiKey) : undefined;
48
+
49
+ return new BaseProvider({
50
+ name: 'local-llm',
51
+ models: allowedModels,
52
+ pricing,
53
+ relay: {
54
+ baseUrl,
55
+ authHeaderName: 'authorization',
56
+ authHeaderValue: apiKey ? `Bearer ${apiKey}` : '',
57
+ tokenProvider,
58
+ maxConcurrency,
59
+ allowedModels,
60
+ },
61
+ });
62
+ },
63
+ };
64
+
65
+ export default plugin;
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["src/**/*.test.ts"]
9
+ }