portos-ai-toolkit 0.4.2 → 0.5.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/package.json
CHANGED
package/src/server/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface ProviderService {
|
|
|
12
12
|
deleteProvider(id: string): Promise<boolean>;
|
|
13
13
|
testProvider(id: string): Promise<{ success: boolean; [key: string]: any }>;
|
|
14
14
|
refreshProviderModels(id: string): Promise<any | null>;
|
|
15
|
+
getSampleProviders(): Promise<any[]>;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface RunnerService {
|
package/src/server/providers.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { join, dirname } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import { exec } from 'child_process';
|
|
5
6
|
import { promisify } from 'util';
|
|
6
7
|
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const DEFAULT_SAMPLE_PATH = join(__dirname, '../defaults/providers.sample.json');
|
|
10
|
+
|
|
7
11
|
const execAsync = promisify(exec);
|
|
8
12
|
|
|
9
13
|
/**
|
|
@@ -384,6 +388,33 @@ export function createProviderService(config = {}) {
|
|
|
384
388
|
return (data.models || [])
|
|
385
389
|
.filter(m => m.supportedGenerationMethods?.includes('generateContent'))
|
|
386
390
|
.map(m => m.name.replace('models/', ''));
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Get sample providers not yet in user's configuration.
|
|
395
|
+
* Reads toolkit's built-in defaults and overlays with host app's sample file.
|
|
396
|
+
*/
|
|
397
|
+
async getSampleProviders() {
|
|
398
|
+
const data = await loadProviders();
|
|
399
|
+
const existingIds = new Set(Object.keys(data.providers));
|
|
400
|
+
|
|
401
|
+
// Read toolkit's built-in sample
|
|
402
|
+
let sampleProviders = {};
|
|
403
|
+
if (existsSync(DEFAULT_SAMPLE_PATH)) {
|
|
404
|
+
const content = await readFile(DEFAULT_SAMPLE_PATH, 'utf-8');
|
|
405
|
+
const parsed = JSON.parse(content);
|
|
406
|
+
sampleProviders = { ...parsed.providers };
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Overlay with host app's sample (takes precedence)
|
|
410
|
+
if (sampleFile && existsSync(sampleFile)) {
|
|
411
|
+
const content = await readFile(sampleFile, 'utf-8');
|
|
412
|
+
const parsed = JSON.parse(content);
|
|
413
|
+
sampleProviders = { ...sampleProviders, ...parsed.providers };
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Filter out providers already in user's config
|
|
417
|
+
return Object.values(sampleProviders).filter(p => !existingIds.has(p.id));
|
|
387
418
|
}
|
|
388
419
|
};
|
|
389
420
|
}
|
|
@@ -117,4 +117,75 @@ describe('Provider Service', () => {
|
|
|
117
117
|
})
|
|
118
118
|
).rejects.toThrow('Provider with this ID already exists');
|
|
119
119
|
});
|
|
120
|
+
|
|
121
|
+
describe('getSampleProviders', () => {
|
|
122
|
+
it('should return sample providers from default sample file', async () => {
|
|
123
|
+
// No providers created yet — all samples should be returned
|
|
124
|
+
const samples = await providerService.getSampleProviders();
|
|
125
|
+
expect(Array.isArray(samples)).toBe(true);
|
|
126
|
+
expect(samples.length).toBeGreaterThan(0);
|
|
127
|
+
// Should include claude-code-bedrock from the default sample
|
|
128
|
+
const bedrock = samples.find(p => p.id === 'claude-code-bedrock');
|
|
129
|
+
expect(bedrock).toBeDefined();
|
|
130
|
+
expect(bedrock.name).toBe('Claude Code CLI: Bedrock');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should exclude providers already in user config', async () => {
|
|
134
|
+
// Create a provider with an ID that matches a sample
|
|
135
|
+
await providerService.createProvider({
|
|
136
|
+
id: 'claude-code',
|
|
137
|
+
name: 'Claude Code CLI',
|
|
138
|
+
type: 'cli',
|
|
139
|
+
command: 'claude'
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const samples = await providerService.getSampleProviders();
|
|
143
|
+
const claudeCode = samples.find(p => p.id === 'claude-code');
|
|
144
|
+
expect(claudeCode).toBeUndefined();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should overlay host app sample over toolkit defaults', async () => {
|
|
148
|
+
// Pre-create providers.json with one existing provider so loadProviders
|
|
149
|
+
// doesn't bootstrap from sampleFile
|
|
150
|
+
const providersPath = join(TEST_DATA_DIR, 'providers-overlay.json');
|
|
151
|
+
await writeFile(providersPath, JSON.stringify({
|
|
152
|
+
activeProvider: 'existing',
|
|
153
|
+
providers: {
|
|
154
|
+
existing: { id: 'existing', name: 'Existing', type: 'cli', command: 'test' }
|
|
155
|
+
}
|
|
156
|
+
}));
|
|
157
|
+
|
|
158
|
+
// Create a host app sample with a unique provider
|
|
159
|
+
const samplePath = join(TEST_DATA_DIR, 'custom-sample.json');
|
|
160
|
+
await writeFile(samplePath, JSON.stringify({
|
|
161
|
+
activeProvider: 'custom-cli',
|
|
162
|
+
providers: {
|
|
163
|
+
'custom-cli': {
|
|
164
|
+
id: 'custom-cli',
|
|
165
|
+
name: 'Custom CLI',
|
|
166
|
+
type: 'cli',
|
|
167
|
+
command: 'custom',
|
|
168
|
+
args: [],
|
|
169
|
+
models: [],
|
|
170
|
+
timeout: 300000,
|
|
171
|
+
enabled: true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}));
|
|
175
|
+
|
|
176
|
+
const serviceWithSample = createProviderService({
|
|
177
|
+
dataDir: TEST_DATA_DIR,
|
|
178
|
+
providersFile: 'providers-overlay.json',
|
|
179
|
+
sampleFile: samplePath
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const samples = await serviceWithSample.getSampleProviders();
|
|
183
|
+
const custom = samples.find(p => p.id === 'custom-cli');
|
|
184
|
+
expect(custom).toBeDefined();
|
|
185
|
+
expect(custom.name).toBe('Custom CLI');
|
|
186
|
+
// 'existing' should NOT appear (already in user's config)
|
|
187
|
+
const existing = samples.find(p => p.id === 'existing');
|
|
188
|
+
expect(existing).toBeUndefined();
|
|
189
|
+
});
|
|
190
|
+
});
|
|
120
191
|
});
|
|
@@ -35,6 +35,12 @@ export function createProvidersRoutes(providerService, options = {}) {
|
|
|
35
35
|
res.json(provider);
|
|
36
36
|
}));
|
|
37
37
|
|
|
38
|
+
// GET /providers/samples - Get sample providers not yet in user's config
|
|
39
|
+
router.get('/samples', asyncHandler(async (req, res) => {
|
|
40
|
+
const providers = await providerService.getSampleProviders();
|
|
41
|
+
res.json({ providers });
|
|
42
|
+
}));
|
|
43
|
+
|
|
38
44
|
// GET /providers/:id - Get provider by ID
|
|
39
45
|
router.get('/:id', asyncHandler(async (req, res) => {
|
|
40
46
|
const provider = await providerService.getProviderById(req.params.id);
|