@antseed/provider-openrouter 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,31 @@
1
+ # @antseed/provider-openrouter
2
+
3
+ Sell OpenRouter API capacity on the Antseed P2P network. Supports multiple LLM providers through a single API key.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ antseed plugin add @antseed/provider-openrouter
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ export OPENROUTER_API_KEY=sk-or-...
15
+ antseed seed --provider openrouter
16
+ ```
17
+
18
+ ## Configuration
19
+
20
+ | Key | Type | Required | Default | Description |
21
+ |-----|------|----------|---------|-------------|
22
+ | `OPENROUTER_API_KEY` | secret | Yes | -- | OpenRouter API key |
23
+ | `ANTSEED_INPUT_USD_PER_MILLION` | number | No | 10 | Input token price (USD per 1M) |
24
+ | `ANTSEED_OUTPUT_USD_PER_MILLION` | number | No | 10 | Output token price (USD per 1M) |
25
+ | `ANTSEED_MODEL_PRICING_JSON` | string | No | -- | Per-model pricing as JSON |
26
+ | `ANTSEED_MAX_CONCURRENCY` | number | No | 10 | Max concurrent requests |
27
+ | `ANTSEED_ALLOWED_MODELS` | string[] | No | -- | Comma-separated model allowlist |
28
+
29
+ ## How It Works
30
+
31
+ Uses `BaseProvider` and `StaticTokenProvider` from `@antseed/provider-core` to relay requests to the OpenRouter API with `Authorization: Bearer` authentication.
@@ -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;AA4CrE,QAAA,MAAM,MAAM,EAAE,qBAuDb,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,90 @@
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
+ function parseModelPricingJson(raw) {
10
+ if (!raw)
11
+ return undefined;
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(raw);
15
+ }
16
+ catch {
17
+ throw new Error('ANTSEED_MODEL_PRICING_JSON must be valid JSON');
18
+ }
19
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
20
+ throw new Error('ANTSEED_MODEL_PRICING_JSON must be an object map of model -> pricing');
21
+ }
22
+ const out = {};
23
+ for (const [model, pricing] of Object.entries(parsed)) {
24
+ if (!pricing || typeof pricing !== 'object' || Array.isArray(pricing)) {
25
+ throw new Error(`Model pricing for "${model}" must be an object`);
26
+ }
27
+ const input = pricing['inputUsdPerMillion'];
28
+ const output = pricing['outputUsdPerMillion'];
29
+ if (typeof input !== 'number' || !Number.isFinite(input) || input < 0) {
30
+ throw new Error(`Model pricing for "${model}" requires non-negative inputUsdPerMillion`);
31
+ }
32
+ if (typeof output !== 'number' || !Number.isFinite(output) || output < 0) {
33
+ throw new Error(`Model pricing for "${model}" requires non-negative outputUsdPerMillion`);
34
+ }
35
+ out[model] = { inputUsdPerMillion: input, outputUsdPerMillion: output };
36
+ }
37
+ return Object.keys(out).length > 0 ? out : undefined;
38
+ }
39
+ const plugin = {
40
+ name: 'openrouter',
41
+ displayName: 'OpenRouter',
42
+ version: '0.1.0',
43
+ type: 'provider',
44
+ description: 'Sell OpenRouter API capacity to P2P peers',
45
+ configSchema: [
46
+ { key: 'OPENROUTER_API_KEY', label: 'API Key', type: 'secret', required: true, description: 'OpenRouter API key' },
47
+ { key: 'ANTSEED_INPUT_USD_PER_MILLION', label: 'Input Price', type: 'number', required: false, default: 10, description: 'Input price in USD per 1M tokens' },
48
+ { key: 'ANTSEED_OUTPUT_USD_PER_MILLION', label: 'Output Price', type: 'number', required: false, default: 10, description: 'Output price in USD per 1M tokens' },
49
+ { key: 'ANTSEED_MODEL_PRICING_JSON', label: 'Model Pricing JSON', type: 'string', required: false, description: 'Per-model pricing JSON' },
50
+ { key: 'ANTSEED_MAX_CONCURRENCY', label: 'Max Concurrency', type: 'number', required: false, default: 10, description: 'Max concurrent requests' },
51
+ { key: 'ANTSEED_ALLOWED_MODELS', label: 'Allowed Models', type: 'string[]', required: false, description: 'Model allow-list' },
52
+ ],
53
+ createProvider(config) {
54
+ const apiKey = config['OPENROUTER_API_KEY'] ?? '';
55
+ if (!apiKey) {
56
+ throw new Error('OPENROUTER_API_KEY is required');
57
+ }
58
+ const modelPricing = parseModelPricingJson(config['ANTSEED_MODEL_PRICING_JSON']);
59
+ const pricing = {
60
+ defaults: {
61
+ inputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_INPUT_USD_PER_MILLION'], 'ANTSEED_INPUT_USD_PER_MILLION', 10),
62
+ outputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_OUTPUT_USD_PER_MILLION'], 'ANTSEED_OUTPUT_USD_PER_MILLION', 10),
63
+ },
64
+ ...(modelPricing ? { models: modelPricing } : {}),
65
+ };
66
+ const maxConcurrency = parseInt(config['ANTSEED_MAX_CONCURRENCY'] ?? '10', 10);
67
+ if (Number.isNaN(maxConcurrency)) {
68
+ throw new Error('ANTSEED_MAX_CONCURRENCY must be a valid number');
69
+ }
70
+ const allowedModels = config['ANTSEED_ALLOWED_MODELS']
71
+ ? config['ANTSEED_ALLOWED_MODELS'].split(',').map((s) => s.trim())
72
+ : [];
73
+ const tokenProvider = new StaticTokenProvider(apiKey);
74
+ return new BaseProvider({
75
+ name: 'openrouter',
76
+ models: allowedModels,
77
+ pricing,
78
+ relay: {
79
+ baseUrl: 'https://openrouter.ai/api',
80
+ authHeaderName: 'authorization',
81
+ authHeaderValue: `Bearer ${apiKey}`,
82
+ tokenProvider,
83
+ maxConcurrency,
84
+ allowedModels,
85
+ },
86
+ });
87
+ },
88
+ };
89
+ export default plugin;
90
+ //# 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-openrouter",
3
+ "version": "0.1.0",
4
+ "description": "OpenRouter 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,46 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import plugin from './index.js';
3
+
4
+ describe('provider-openrouter plugin', () => {
5
+ it('has correct name and metadata', () => {
6
+ expect(plugin.name).toBe('openrouter');
7
+ expect(plugin.displayName).toBe('OpenRouter');
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('OPENROUTER_API_KEY');
15
+ expect(keys).toContain('ANTSEED_INPUT_USD_PER_MILLION');
16
+ expect(keys).toContain('ANTSEED_OUTPUT_USD_PER_MILLION');
17
+ expect(keys).toContain('ANTSEED_MAX_CONCURRENCY');
18
+ expect(keys).toContain('ANTSEED_ALLOWED_MODELS');
19
+ });
20
+
21
+ it('creates provider with valid config', () => {
22
+ const provider = plugin.createProvider({
23
+ OPENROUTER_API_KEY: 'sk-or-test-key',
24
+ });
25
+ expect(provider.name).toBe('openrouter');
26
+ expect(provider.pricing.defaults.inputUsdPerMillion).toBe(10);
27
+ expect(provider.pricing.defaults.outputUsdPerMillion).toBe(10);
28
+ expect(provider.maxConcurrency).toBe(10);
29
+ });
30
+
31
+ it('requires API key', () => {
32
+ expect(() => plugin.createProvider({})).toThrow('OPENROUTER_API_KEY is required');
33
+ });
34
+
35
+ it('applies custom pricing and concurrency', () => {
36
+ const provider = plugin.createProvider({
37
+ OPENROUTER_API_KEY: 'sk-or-test-key',
38
+ ANTSEED_INPUT_USD_PER_MILLION: '3',
39
+ ANTSEED_OUTPUT_USD_PER_MILLION: '7',
40
+ ANTSEED_MAX_CONCURRENCY: '5',
41
+ });
42
+ expect(provider.pricing.defaults.inputUsdPerMillion).toBe(3);
43
+ expect(provider.pricing.defaults.outputUsdPerMillion).toBe(7);
44
+ expect(provider.maxConcurrency).toBe(5);
45
+ });
46
+ });
package/src/index.ts ADDED
@@ -0,0 +1,102 @@
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
+ function parseModelPricingJson(raw: string | undefined): Provider['pricing']['models'] {
13
+ if (!raw) return undefined;
14
+
15
+ let parsed: unknown;
16
+ try {
17
+ parsed = JSON.parse(raw) as unknown;
18
+ } catch {
19
+ throw new Error('ANTSEED_MODEL_PRICING_JSON must be valid JSON');
20
+ }
21
+
22
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
23
+ throw new Error('ANTSEED_MODEL_PRICING_JSON must be an object map of model -> pricing');
24
+ }
25
+
26
+ const out: NonNullable<Provider['pricing']['models']> = {};
27
+ for (const [model, pricing] of Object.entries(parsed as Record<string, unknown>)) {
28
+ if (!pricing || typeof pricing !== 'object' || Array.isArray(pricing)) {
29
+ throw new Error(`Model pricing for "${model}" must be an object`);
30
+ }
31
+ const input = (pricing as Record<string, unknown>)['inputUsdPerMillion'];
32
+ const output = (pricing as Record<string, unknown>)['outputUsdPerMillion'];
33
+ if (typeof input !== 'number' || !Number.isFinite(input) || input < 0) {
34
+ throw new Error(`Model pricing for "${model}" requires non-negative inputUsdPerMillion`);
35
+ }
36
+ if (typeof output !== 'number' || !Number.isFinite(output) || output < 0) {
37
+ throw new Error(`Model pricing for "${model}" requires non-negative outputUsdPerMillion`);
38
+ }
39
+ out[model] = { inputUsdPerMillion: input, outputUsdPerMillion: output };
40
+ }
41
+
42
+ return Object.keys(out).length > 0 ? out : undefined;
43
+ }
44
+
45
+ const plugin: AntseedProviderPlugin = {
46
+ name: 'openrouter',
47
+ displayName: 'OpenRouter',
48
+ version: '0.1.0',
49
+ type: 'provider',
50
+ description: 'Sell OpenRouter API capacity to P2P peers',
51
+ configSchema: [
52
+ { key: 'OPENROUTER_API_KEY', label: 'API Key', type: 'secret', required: true, description: 'OpenRouter API key' },
53
+ { key: 'ANTSEED_INPUT_USD_PER_MILLION', label: 'Input Price', type: 'number', required: false, default: 10, description: 'Input price in USD per 1M tokens' },
54
+ { key: 'ANTSEED_OUTPUT_USD_PER_MILLION', label: 'Output Price', type: 'number', required: false, default: 10, description: 'Output price in USD per 1M tokens' },
55
+ { key: 'ANTSEED_MODEL_PRICING_JSON', label: 'Model Pricing JSON', type: 'string', required: false, description: 'Per-model pricing JSON' },
56
+ { key: 'ANTSEED_MAX_CONCURRENCY', label: 'Max Concurrency', type: 'number', required: false, default: 10, description: 'Max concurrent requests' },
57
+ { key: 'ANTSEED_ALLOWED_MODELS', label: 'Allowed Models', type: 'string[]', required: false, description: 'Model allow-list' },
58
+ ],
59
+
60
+ createProvider(config: Record<string, string>): Provider {
61
+ const apiKey = config['OPENROUTER_API_KEY'] ?? '';
62
+ if (!apiKey) {
63
+ throw new Error('OPENROUTER_API_KEY is required');
64
+ }
65
+
66
+ const modelPricing = parseModelPricingJson(config['ANTSEED_MODEL_PRICING_JSON']);
67
+ const pricing: Provider['pricing'] = {
68
+ defaults: {
69
+ inputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_INPUT_USD_PER_MILLION'], 'ANTSEED_INPUT_USD_PER_MILLION', 10),
70
+ outputUsdPerMillion: parseNonNegativeNumber(config['ANTSEED_OUTPUT_USD_PER_MILLION'], 'ANTSEED_OUTPUT_USD_PER_MILLION', 10),
71
+ },
72
+ ...(modelPricing ? { models: modelPricing } : {}),
73
+ };
74
+
75
+ const maxConcurrency = parseInt(config['ANTSEED_MAX_CONCURRENCY'] ?? '10', 10);
76
+ if (Number.isNaN(maxConcurrency)) {
77
+ throw new Error('ANTSEED_MAX_CONCURRENCY must be a valid number');
78
+ }
79
+
80
+ const allowedModels = config['ANTSEED_ALLOWED_MODELS']
81
+ ? config['ANTSEED_ALLOWED_MODELS'].split(',').map((s: string) => s.trim())
82
+ : [];
83
+
84
+ const tokenProvider = new StaticTokenProvider(apiKey);
85
+
86
+ return new BaseProvider({
87
+ name: 'openrouter',
88
+ models: allowedModels,
89
+ pricing,
90
+ relay: {
91
+ baseUrl: 'https://openrouter.ai/api',
92
+ authHeaderName: 'authorization',
93
+ authHeaderValue: `Bearer ${apiKey}`,
94
+ tokenProvider,
95
+ maxConcurrency,
96
+ allowedModels,
97
+ },
98
+ });
99
+ },
100
+ };
101
+
102
+ 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
+ }