@wopr-network/platform-core 1.10.0 → 1.11.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.
@@ -12,11 +12,10 @@
12
12
  * - tts (Chatterbox GPU, ElevenLabs)
13
13
  * - transcription (Deepgram)
14
14
  * - embeddings (OpenRouter)
15
- *
16
- * Image-generation (Nano Banana, Replicate) will be wired once the
17
- * image-gen-factory PR merges.
15
+ * - image-generation (Replicate, Nano Banana)
18
16
  */
19
17
  import { type EmbeddingsFactoryConfig } from "./embeddings-factory.js";
18
+ import { type ImageGenFactoryConfig } from "./image-gen-factory.js";
20
19
  import { type TextGenFactoryConfig } from "./text-gen-factory.js";
21
20
  import { type TranscriptionFactoryConfig } from "./transcription-factory.js";
22
21
  import { type TTSFactoryConfig } from "./tts-factory.js";
@@ -31,6 +30,8 @@ export interface BootstrapConfig {
31
30
  transcription?: TranscriptionFactoryConfig;
32
31
  /** Embeddings adapter config */
33
32
  embeddings?: EmbeddingsFactoryConfig;
33
+ /** Image generation adapter config */
34
+ imageGen?: ImageGenFactoryConfig;
34
35
  }
35
36
  /** Result of bootstrapping all adapters. */
36
37
  export interface BootstrapResult {
@@ -42,6 +43,12 @@ export interface BootstrapResult {
42
43
  * capabilities (e.g. OpenRouter for both text-gen and embeddings). Each
43
44
  * instance is independently configured. Use the per-capability factory
44
45
  * results if you need a name→adapter map within a single capability.
46
+ *
47
+ * NOTE: Some adapters advertise more capabilities than the factory that
48
+ * created them (e.g. Replicate advertises text-generation and transcription
49
+ * in addition to image-generation). The ArbitrageRouter should use the
50
+ * factory-assigned capability (reflected in byCapability), not the adapter's
51
+ * own capabilities array, for routing decisions.
45
52
  */
46
53
  adapters: ProviderAdapter[];
47
54
  /** Names of providers that were skipped (missing config), grouped by capability. */
@@ -68,6 +75,7 @@ export declare function bootstrapAdapters(config: BootstrapConfig): BootstrapRes
68
75
  * - CHATTERBOX_BASE_URL, ELEVENLABS_API_KEY (TTS)
69
76
  * - DEEPGRAM_API_KEY (transcription)
70
77
  * - OPENROUTER_API_KEY (embeddings)
78
+ * - REPLICATE_API_TOKEN, NANO_BANANA_API_KEY (image-gen)
71
79
  *
72
80
  * Accepts optional per-capability config overrides.
73
81
  */
@@ -12,11 +12,10 @@
12
12
  * - tts (Chatterbox GPU, ElevenLabs)
13
13
  * - transcription (Deepgram)
14
14
  * - embeddings (OpenRouter)
15
- *
16
- * Image-generation (Nano Banana, Replicate) will be wired once the
17
- * image-gen-factory PR merges.
15
+ * - image-generation (Replicate, Nano Banana)
18
16
  */
19
17
  import { createEmbeddingsAdapters } from "./embeddings-factory.js";
18
+ import { createImageGenAdapters } from "./image-gen-factory.js";
20
19
  import { createTextGenAdapters } from "./text-gen-factory.js";
21
20
  import { createTranscriptionAdapters } from "./transcription-factory.js";
22
21
  import { createTTSAdapters } from "./tts-factory.js";
@@ -31,11 +30,13 @@ export function bootstrapAdapters(config) {
31
30
  const tts = createTTSAdapters(config.tts ?? {});
32
31
  const transcription = createTranscriptionAdapters(config.transcription ?? {});
33
32
  const embeddings = createEmbeddingsAdapters(config.embeddings ?? {});
33
+ const imageGen = createImageGenAdapters(config.imageGen ?? {});
34
34
  const adapters = [
35
35
  ...textGen.adapters,
36
36
  ...tts.adapters,
37
37
  ...transcription.adapters,
38
38
  ...embeddings.adapters,
39
+ ...imageGen.adapters,
39
40
  ];
40
41
  const skipped = {};
41
42
  if (textGen.skipped.length > 0)
@@ -46,6 +47,8 @@ export function bootstrapAdapters(config) {
46
47
  skipped.transcription = transcription.skipped;
47
48
  if (embeddings.skipped.length > 0)
48
49
  skipped.embeddings = embeddings.skipped;
50
+ if (imageGen.skipped.length > 0)
51
+ skipped["image-generation"] = imageGen.skipped;
49
52
  let totalSkipped = 0;
50
53
  for (const list of Object.values(skipped)) {
51
54
  totalSkipped += list.length;
@@ -61,6 +64,7 @@ export function bootstrapAdapters(config) {
61
64
  tts: tts.adapters.length,
62
65
  transcription: transcription.adapters.length,
63
66
  embeddings: embeddings.adapters.length,
67
+ "image-generation": imageGen.adapters.length,
64
68
  },
65
69
  },
66
70
  };
@@ -73,6 +77,7 @@ export function bootstrapAdapters(config) {
73
77
  * - CHATTERBOX_BASE_URL, ELEVENLABS_API_KEY (TTS)
74
78
  * - DEEPGRAM_API_KEY (transcription)
75
79
  * - OPENROUTER_API_KEY (embeddings)
80
+ * - REPLICATE_API_TOKEN, NANO_BANANA_API_KEY (image-gen)
76
81
  *
77
82
  * Accepts optional per-capability config overrides.
78
83
  */
@@ -99,5 +104,12 @@ export function bootstrapAdaptersFromEnv(overrides) {
99
104
  openrouterApiKey: process.env.OPENROUTER_API_KEY,
100
105
  ...overrides?.embeddings,
101
106
  },
107
+ imageGen: {
108
+ replicateApiToken: process.env.REPLICATE_API_TOKEN,
109
+ // Separate env var from GEMINI_API_KEY (used for text-gen) to avoid
110
+ // silently enabling image-gen when only text-gen is intended.
111
+ geminiApiKey: process.env.NANO_BANANA_API_KEY,
112
+ ...overrides?.imageGen,
113
+ },
102
114
  });
103
115
  }
@@ -20,10 +20,14 @@ describe("bootstrapAdapters", () => {
20
20
  embeddings: {
21
21
  openrouterApiKey: "sk-or",
22
22
  },
23
+ imageGen: {
24
+ replicateApiToken: "r8-rep",
25
+ geminiApiKey: "sk-gem",
26
+ },
23
27
  });
24
- // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings = 9
25
- expect(result.adapters).toHaveLength(9);
26
- expect(result.summary.total).toBe(9);
28
+ // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings + 2 image-gen = 11
29
+ expect(result.adapters).toHaveLength(11);
30
+ expect(result.summary.total).toBe(11);
27
31
  expect(result.summary.skipped).toBe(0);
28
32
  });
29
33
  it("allows duplicate provider names across capabilities", () => {
@@ -48,6 +52,7 @@ describe("bootstrapAdapters", () => {
48
52
  tts: 1,
49
53
  transcription: 1,
50
54
  embeddings: 1,
55
+ "image-generation": 0,
51
56
  });
52
57
  });
53
58
  it("tracks skipped providers by capability", () => {
@@ -61,6 +66,7 @@ describe("bootstrapAdapters", () => {
61
66
  expect(result.skipped.transcription).toEqual(["deepgram"]);
62
67
  expect(result.skipped.embeddings).toEqual(["openrouter"]);
63
68
  expect(result.skipped["text-generation"]).toEqual(["gemini", "minimax", "kimi", "openrouter"]);
69
+ expect(result.skipped["image-generation"]).toEqual(["replicate", "nano-banana"]);
64
70
  });
65
71
  it("returns empty result when no config provided", () => {
66
72
  const result = bootstrapAdapters({});
@@ -82,6 +88,7 @@ describe("bootstrapAdapters", () => {
82
88
  expect(result.summary.byCapability.tts).toBe(0);
83
89
  expect(result.summary.byCapability.transcription).toBe(0);
84
90
  expect(result.summary.byCapability.embeddings).toBe(0);
91
+ expect(result.summary.byCapability["image-generation"]).toBe(0);
85
92
  });
86
93
  it("passes per-adapter overrides through", () => {
87
94
  const result = bootstrapAdapters({
@@ -110,10 +117,12 @@ describe("bootstrapAdaptersFromEnv", () => {
110
117
  vi.stubEnv("CHATTERBOX_BASE_URL", "http://chatterbox:8000");
111
118
  vi.stubEnv("ELEVENLABS_API_KEY", "env-el");
112
119
  vi.stubEnv("DEEPGRAM_API_KEY", "env-dg");
120
+ vi.stubEnv("REPLICATE_API_TOKEN", "r8-rep");
121
+ vi.stubEnv("NANO_BANANA_API_KEY", "env-nb");
113
122
  const result = bootstrapAdaptersFromEnv();
114
- // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings = 9
115
- expect(result.adapters).toHaveLength(9);
116
- expect(result.summary.total).toBe(9);
123
+ // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings + 2 image-gen = 11
124
+ expect(result.adapters).toHaveLength(11);
125
+ expect(result.summary.total).toBe(11);
117
126
  });
118
127
  it("returns empty when no env vars set", () => {
119
128
  vi.stubEnv("DEEPSEEK_API_KEY", "");
@@ -124,6 +133,8 @@ describe("bootstrapAdaptersFromEnv", () => {
124
133
  vi.stubEnv("CHATTERBOX_BASE_URL", "");
125
134
  vi.stubEnv("ELEVENLABS_API_KEY", "");
126
135
  vi.stubEnv("DEEPGRAM_API_KEY", "");
136
+ vi.stubEnv("REPLICATE_API_TOKEN", "");
137
+ vi.stubEnv("NANO_BANANA_API_KEY", "");
127
138
  const result = bootstrapAdaptersFromEnv();
128
139
  expect(result.adapters).toHaveLength(0);
129
140
  expect(result.summary.skipped).toBeGreaterThan(0);
@@ -137,6 +148,8 @@ describe("bootstrapAdaptersFromEnv", () => {
137
148
  vi.stubEnv("CHATTERBOX_BASE_URL", "");
138
149
  vi.stubEnv("ELEVENLABS_API_KEY", "");
139
150
  vi.stubEnv("DEEPGRAM_API_KEY", "");
151
+ vi.stubEnv("REPLICATE_API_TOKEN", "");
152
+ vi.stubEnv("NANO_BANANA_API_KEY", "");
140
153
  const result = bootstrapAdaptersFromEnv({
141
154
  textGen: { deepseek: { marginMultiplier: 2.0 } },
142
155
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/platform-core",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,11 +21,15 @@ describe("bootstrapAdapters", () => {
21
21
  embeddings: {
22
22
  openrouterApiKey: "sk-or",
23
23
  },
24
+ imageGen: {
25
+ replicateApiToken: "r8-rep",
26
+ geminiApiKey: "sk-gem",
27
+ },
24
28
  });
25
29
 
26
- // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings = 9
27
- expect(result.adapters).toHaveLength(9);
28
- expect(result.summary.total).toBe(9);
30
+ // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings + 2 image-gen = 11
31
+ expect(result.adapters).toHaveLength(11);
32
+ expect(result.summary.total).toBe(11);
29
33
  expect(result.summary.skipped).toBe(0);
30
34
  });
31
35
 
@@ -54,6 +58,7 @@ describe("bootstrapAdapters", () => {
54
58
  tts: 1,
55
59
  transcription: 1,
56
60
  embeddings: 1,
61
+ "image-generation": 0,
57
62
  });
58
63
  });
59
64
 
@@ -69,6 +74,7 @@ describe("bootstrapAdapters", () => {
69
74
  expect(result.skipped.transcription).toEqual(["deepgram"]);
70
75
  expect(result.skipped.embeddings).toEqual(["openrouter"]);
71
76
  expect(result.skipped["text-generation"]).toEqual(["gemini", "minimax", "kimi", "openrouter"]);
77
+ expect(result.skipped["image-generation"]).toEqual(["replicate", "nano-banana"]);
72
78
  });
73
79
 
74
80
  it("returns empty result when no config provided", () => {
@@ -96,6 +102,7 @@ describe("bootstrapAdapters", () => {
96
102
  expect(result.summary.byCapability.tts).toBe(0);
97
103
  expect(result.summary.byCapability.transcription).toBe(0);
98
104
  expect(result.summary.byCapability.embeddings).toBe(0);
105
+ expect(result.summary.byCapability["image-generation"]).toBe(0);
99
106
  });
100
107
 
101
108
  it("passes per-adapter overrides through", () => {
@@ -129,12 +136,14 @@ describe("bootstrapAdaptersFromEnv", () => {
129
136
  vi.stubEnv("CHATTERBOX_BASE_URL", "http://chatterbox:8000");
130
137
  vi.stubEnv("ELEVENLABS_API_KEY", "env-el");
131
138
  vi.stubEnv("DEEPGRAM_API_KEY", "env-dg");
139
+ vi.stubEnv("REPLICATE_API_TOKEN", "r8-rep");
140
+ vi.stubEnv("NANO_BANANA_API_KEY", "env-nb");
132
141
 
133
142
  const result = bootstrapAdaptersFromEnv();
134
143
 
135
- // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings = 9
136
- expect(result.adapters).toHaveLength(9);
137
- expect(result.summary.total).toBe(9);
144
+ // 5 text-gen + 2 TTS + 1 transcription + 1 embeddings + 2 image-gen = 11
145
+ expect(result.adapters).toHaveLength(11);
146
+ expect(result.summary.total).toBe(11);
138
147
  });
139
148
 
140
149
  it("returns empty when no env vars set", () => {
@@ -146,6 +155,8 @@ describe("bootstrapAdaptersFromEnv", () => {
146
155
  vi.stubEnv("CHATTERBOX_BASE_URL", "");
147
156
  vi.stubEnv("ELEVENLABS_API_KEY", "");
148
157
  vi.stubEnv("DEEPGRAM_API_KEY", "");
158
+ vi.stubEnv("REPLICATE_API_TOKEN", "");
159
+ vi.stubEnv("NANO_BANANA_API_KEY", "");
149
160
 
150
161
  const result = bootstrapAdaptersFromEnv();
151
162
 
@@ -162,6 +173,8 @@ describe("bootstrapAdaptersFromEnv", () => {
162
173
  vi.stubEnv("CHATTERBOX_BASE_URL", "");
163
174
  vi.stubEnv("ELEVENLABS_API_KEY", "");
164
175
  vi.stubEnv("DEEPGRAM_API_KEY", "");
176
+ vi.stubEnv("REPLICATE_API_TOKEN", "");
177
+ vi.stubEnv("NANO_BANANA_API_KEY", "");
165
178
 
166
179
  const result = bootstrapAdaptersFromEnv({
167
180
  textGen: { deepseek: { marginMultiplier: 2.0 } },
@@ -12,12 +12,11 @@
12
12
  * - tts (Chatterbox GPU, ElevenLabs)
13
13
  * - transcription (Deepgram)
14
14
  * - embeddings (OpenRouter)
15
- *
16
- * Image-generation (Nano Banana, Replicate) will be wired once the
17
- * image-gen-factory PR merges.
15
+ * - image-generation (Replicate, Nano Banana)
18
16
  */
19
17
 
20
18
  import { createEmbeddingsAdapters, type EmbeddingsFactoryConfig } from "./embeddings-factory.js";
19
+ import { createImageGenAdapters, type ImageGenFactoryConfig } from "./image-gen-factory.js";
21
20
  import { createTextGenAdapters, type TextGenFactoryConfig } from "./text-gen-factory.js";
22
21
  import { createTranscriptionAdapters, type TranscriptionFactoryConfig } from "./transcription-factory.js";
23
22
  import { createTTSAdapters, type TTSFactoryConfig } from "./tts-factory.js";
@@ -33,6 +32,8 @@ export interface BootstrapConfig {
33
32
  transcription?: TranscriptionFactoryConfig;
34
33
  /** Embeddings adapter config */
35
34
  embeddings?: EmbeddingsFactoryConfig;
35
+ /** Image generation adapter config */
36
+ imageGen?: ImageGenFactoryConfig;
36
37
  }
37
38
 
38
39
  /** Result of bootstrapping all adapters. */
@@ -45,6 +46,12 @@ export interface BootstrapResult {
45
46
  * capabilities (e.g. OpenRouter for both text-gen and embeddings). Each
46
47
  * instance is independently configured. Use the per-capability factory
47
48
  * results if you need a name→adapter map within a single capability.
49
+ *
50
+ * NOTE: Some adapters advertise more capabilities than the factory that
51
+ * created them (e.g. Replicate advertises text-generation and transcription
52
+ * in addition to image-generation). The ArbitrageRouter should use the
53
+ * factory-assigned capability (reflected in byCapability), not the adapter's
54
+ * own capabilities array, for routing decisions.
48
55
  */
49
56
  adapters: ProviderAdapter[];
50
57
  /** Names of providers that were skipped (missing config), grouped by capability. */
@@ -68,12 +75,14 @@ export function bootstrapAdapters(config: BootstrapConfig): BootstrapResult {
68
75
  const tts = createTTSAdapters(config.tts ?? {});
69
76
  const transcription = createTranscriptionAdapters(config.transcription ?? {});
70
77
  const embeddings = createEmbeddingsAdapters(config.embeddings ?? {});
78
+ const imageGen = createImageGenAdapters(config.imageGen ?? {});
71
79
 
72
80
  const adapters: ProviderAdapter[] = [
73
81
  ...textGen.adapters,
74
82
  ...tts.adapters,
75
83
  ...transcription.adapters,
76
84
  ...embeddings.adapters,
85
+ ...imageGen.adapters,
77
86
  ];
78
87
 
79
88
  const skipped: Record<string, string[]> = {};
@@ -81,6 +90,7 @@ export function bootstrapAdapters(config: BootstrapConfig): BootstrapResult {
81
90
  if (tts.skipped.length > 0) skipped.tts = tts.skipped;
82
91
  if (transcription.skipped.length > 0) skipped.transcription = transcription.skipped;
83
92
  if (embeddings.skipped.length > 0) skipped.embeddings = embeddings.skipped;
93
+ if (imageGen.skipped.length > 0) skipped["image-generation"] = imageGen.skipped;
84
94
 
85
95
  let totalSkipped = 0;
86
96
  for (const list of Object.values(skipped)) {
@@ -98,6 +108,7 @@ export function bootstrapAdapters(config: BootstrapConfig): BootstrapResult {
98
108
  tts: tts.adapters.length,
99
109
  transcription: transcription.adapters.length,
100
110
  embeddings: embeddings.adapters.length,
111
+ "image-generation": imageGen.adapters.length,
101
112
  },
102
113
  },
103
114
  };
@@ -111,6 +122,7 @@ export function bootstrapAdapters(config: BootstrapConfig): BootstrapResult {
111
122
  * - CHATTERBOX_BASE_URL, ELEVENLABS_API_KEY (TTS)
112
123
  * - DEEPGRAM_API_KEY (transcription)
113
124
  * - OPENROUTER_API_KEY (embeddings)
125
+ * - REPLICATE_API_TOKEN, NANO_BANANA_API_KEY (image-gen)
114
126
  *
115
127
  * Accepts optional per-capability config overrides.
116
128
  */
@@ -137,5 +149,12 @@ export function bootstrapAdaptersFromEnv(overrides?: Partial<BootstrapConfig>):
137
149
  openrouterApiKey: process.env.OPENROUTER_API_KEY,
138
150
  ...overrides?.embeddings,
139
151
  },
152
+ imageGen: {
153
+ replicateApiToken: process.env.REPLICATE_API_TOKEN,
154
+ // Separate env var from GEMINI_API_KEY (used for text-gen) to avoid
155
+ // silently enabling image-gen when only text-gen is intended.
156
+ geminiApiKey: process.env.NANO_BANANA_API_KEY,
157
+ ...overrides?.imageGen,
158
+ },
140
159
  });
141
160
  }