@tamyla/clodo-framework 4.4.0 → 4.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/CHANGELOG.md +2 -1844
- package/README.md +44 -18
- package/dist/cli/commands/add.js +325 -0
- package/dist/config/service-schema-config.js +98 -5
- package/dist/index.js +22 -3
- package/dist/middleware/Composer.js +2 -1
- package/dist/middleware/factories.js +445 -0
- package/dist/middleware/index.js +4 -1
- package/dist/modules/ModuleManager.js +6 -2
- package/dist/routing/EnhancedRouter.js +248 -44
- package/dist/routing/RequestContext.js +393 -0
- package/dist/schema/SchemaManager.js +6 -2
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +79 -223
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +241 -98
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +130 -89
- package/dist/simple-api.js +4 -4
- package/dist/utilities/index.js +134 -1
- package/dist/utils/config/environment-var-normalizer.js +233 -0
- package/dist/validation/environmentGuard.js +172 -0
- package/docs/CHANGELOG.md +1877 -0
- package/docs/api-reference.md +153 -0
- package/package.json +4 -1
- package/scripts/repro-clodo.js +123 -0
- package/templates/ai-worker/package.json +19 -0
- package/templates/ai-worker/src/index.js +160 -0
- package/templates/cron-worker/package.json +19 -0
- package/templates/cron-worker/src/index.js +211 -0
- package/templates/edge-proxy/package.json +18 -0
- package/templates/edge-proxy/src/index.js +150 -0
- package/templates/minimal/package.json +17 -0
- package/templates/minimal/src/index.js +40 -0
- package/templates/queue-processor/package.json +19 -0
- package/templates/queue-processor/src/index.js +213 -0
- package/templates/rest-api/.dev.vars +2 -0
- package/templates/rest-api/package.json +19 -0
- package/templates/rest-api/src/index.js +124 -0
package/docs/api-reference.md
CHANGED
|
@@ -296,6 +296,40 @@ router.registerRoute('GET', '/api/users/:id', async (request, id) => {
|
|
|
296
296
|
});
|
|
297
297
|
```
|
|
298
298
|
|
|
299
|
+
##### Express-like Convenience Methods
|
|
300
|
+
|
|
301
|
+
For familiarity with Express patterns, use these shorthand methods:
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
// GET route
|
|
305
|
+
router.get('/api/users', async (request) => {
|
|
306
|
+
return new Response(JSON.stringify(users));
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// POST route
|
|
310
|
+
router.post('/api/users', async (request) => {
|
|
311
|
+
const userData = await request.json();
|
|
312
|
+
return new Response(JSON.stringify(newUser), { status: 201 });
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// PUT route
|
|
316
|
+
router.put('/api/users/:id', async (request, id) => {
|
|
317
|
+
return new Response(JSON.stringify(updatedUser));
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// PATCH route
|
|
321
|
+
router.patch('/api/users/:id', async (request, id) => {
|
|
322
|
+
return new Response(JSON.stringify(patchedUser));
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// DELETE route
|
|
326
|
+
router.delete('/api/users/:id', async (request, id) => {
|
|
327
|
+
return new Response(JSON.stringify({ success: true }));
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
These methods are aliases to `registerRoute()` and support the same handler signature.
|
|
332
|
+
|
|
299
333
|
##### `handleRequest(method, path, request)`
|
|
300
334
|
```javascript
|
|
301
335
|
const response = await router.handleRequest('GET', '/api/users/123', request);
|
|
@@ -441,6 +475,125 @@ const config = DeploymentManager.generateSecureConfig('client', 'prod');
|
|
|
441
475
|
|
|
442
476
|
## ⚙️ Configuration Management
|
|
443
477
|
|
|
478
|
+
### Service Environment Variables (v4.4.1+)
|
|
479
|
+
|
|
480
|
+
The framework standardizes how environment variables are configured in services using a single recommended flat format.
|
|
481
|
+
|
|
482
|
+
#### Recommended Format: Flat Structure
|
|
483
|
+
|
|
484
|
+
```javascript
|
|
485
|
+
// services-config.json
|
|
486
|
+
{
|
|
487
|
+
"services": [
|
|
488
|
+
{
|
|
489
|
+
"name": "api-service",
|
|
490
|
+
"vars": {
|
|
491
|
+
"API_KEY": "my-api-key",
|
|
492
|
+
"DEBUG": "true",
|
|
493
|
+
"LOG_LEVEL": "info"
|
|
494
|
+
},
|
|
495
|
+
"secrets": ["DB_PASSWORD", "JWT_SECRET"]
|
|
496
|
+
}
|
|
497
|
+
]
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
**Benefits**:
|
|
502
|
+
- Simple and clear
|
|
503
|
+
- Aligns with Wrangler.toml conventions
|
|
504
|
+
- Type-safe with validation
|
|
505
|
+
- No version-specific migrations needed
|
|
506
|
+
|
|
507
|
+
#### Variable Naming Conventions
|
|
508
|
+
|
|
509
|
+
All environment variable names must follow SCREAMING_SNAKE_CASE:
|
|
510
|
+
|
|
511
|
+
```javascript
|
|
512
|
+
// ✅ Valid
|
|
513
|
+
API_KEY // Uppercase, underscores
|
|
514
|
+
DATABASE_URL // Multi-word with underscores
|
|
515
|
+
_INTERNAL_FLAG // Starting with underscore
|
|
516
|
+
VAR_123 // With numbers
|
|
517
|
+
|
|
518
|
+
// ❌ Invalid
|
|
519
|
+
api-key // No hyphens
|
|
520
|
+
api.key // No dots
|
|
521
|
+
apiKey // No camelCase
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
#### Secrets Management
|
|
525
|
+
|
|
526
|
+
Specify which variables contain sensitive data:
|
|
527
|
+
|
|
528
|
+
```javascript
|
|
529
|
+
{
|
|
530
|
+
"services": [
|
|
531
|
+
{
|
|
532
|
+
"name": "api-service",
|
|
533
|
+
"vars": {
|
|
534
|
+
"API_URL": "https://api.example.com", // Non-secret
|
|
535
|
+
"ENVIRONMENT": "production" // Non-secret
|
|
536
|
+
},
|
|
537
|
+
"secrets": ["API_KEY", "DB_PASSWORD"] // Sensitive values
|
|
538
|
+
}
|
|
539
|
+
]
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Then provide secret values at deployment time via environment variables or `.dev.vars` file.
|
|
544
|
+
|
|
545
|
+
#### Deprecation Notice (v4.4.1+)
|
|
546
|
+
|
|
547
|
+
The following formats are deprecated and will be removed in v5.0.0:
|
|
548
|
+
|
|
549
|
+
**Nested Format** (Deprecated):
|
|
550
|
+
```javascript
|
|
551
|
+
// ⚠️ Avoid: Will be removed in v5.0.0
|
|
552
|
+
{
|
|
553
|
+
"environment": {
|
|
554
|
+
"vars": { "API_KEY": "value" },
|
|
555
|
+
"secrets": ["DB_PASSWORD"]
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Per-Environment Format** (Deprecated):
|
|
561
|
+
```javascript
|
|
562
|
+
// ⚠️ Avoid: Will be removed in v5.0.0
|
|
563
|
+
{
|
|
564
|
+
"env": {
|
|
565
|
+
"production": { "vars": { "API_KEY": "prod-key" } },
|
|
566
|
+
"staging": { "vars": { "API_KEY": "staging-key" } }
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
**Migration Path**: See [Environment Variable Standardization Guide](./env-var-standardization.md) for complete migration instructions.
|
|
572
|
+
|
|
573
|
+
#### Using `EnvironmentVarNormalizer`
|
|
574
|
+
|
|
575
|
+
The framework provides automatic normalization for backward compatibility:
|
|
576
|
+
|
|
577
|
+
```javascript
|
|
578
|
+
import { EnvironmentVarNormalizer } from '@tamyla/clodo-framework';
|
|
579
|
+
|
|
580
|
+
// Normalize any config format to standard flat structure
|
|
581
|
+
const normalized = EnvironmentVarNormalizer.normalize(service, {
|
|
582
|
+
warnOnDeprecated: true, // Log deprecation warnings
|
|
583
|
+
throwOnConflict: false // Merge conflicting formats
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
// Validate variable naming conventions
|
|
587
|
+
const validation = EnvironmentVarNormalizer.validateNamingConventions(vars);
|
|
588
|
+
if (!validation.valid) {
|
|
589
|
+
console.error('Variable naming issues:', validation.issues);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Check deprecation timeline
|
|
593
|
+
const timeline = EnvironmentVarNormalizer.getDeprecationTimeline('4.4.1');
|
|
594
|
+
console.log(timeline.v5_0_0.status); // "REMOVAL"
|
|
595
|
+
```
|
|
596
|
+
|
|
444
597
|
### Domain Configuration
|
|
445
598
|
|
|
446
599
|
#### `createDomainConfigSchema(domains)`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tamyla/clodo-framework",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "Reusable framework for Clodo-style software architecture on Cloudflare Workers + D1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"./schema": "./dist/schema/SchemaManager.js",
|
|
18
18
|
"./modules": "./dist/modules/ModuleManager.js",
|
|
19
19
|
"./routing": "./dist/routing/EnhancedRouter.js",
|
|
20
|
+
"./routing/context": "./dist/routing/RequestContext.js",
|
|
20
21
|
"./handlers": "./dist/handlers/GenericRouteHandler.js",
|
|
21
22
|
"./config": "./dist/config/index.js",
|
|
22
23
|
"./config/customers": "./dist/config/customers.js",
|
|
@@ -34,9 +35,11 @@
|
|
|
34
35
|
"./programmatic": "./dist/programmatic/index.js",
|
|
35
36
|
"./api": "./dist/api/index.js",
|
|
36
37
|
"./validation": "./dist/validation/index.js",
|
|
38
|
+
"./validation/env": "./dist/validation/environmentGuard.js",
|
|
37
39
|
"./errors": "./dist/errors/index.js",
|
|
38
40
|
"./testing": "./dist/testing/index.js",
|
|
39
41
|
"./middleware": "./dist/middleware/index.js",
|
|
42
|
+
"./middleware/factories": "./dist/middleware/factories.js",
|
|
40
43
|
"./modules/security": "./dist/modules/security.js",
|
|
41
44
|
"./utilities": "./dist/utilities/index.js",
|
|
42
45
|
"./utilities/storage": "./dist/utilities/storage/r2.js",
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Reproduction script for Express-like router.get/router.post API
|
|
4
|
+
*
|
|
5
|
+
* This script demonstrates both the original registerRoute() API
|
|
6
|
+
* and the new Express-like convenience methods (router.get(), router.post(), etc.)
|
|
7
|
+
*
|
|
8
|
+
* Run: node scripts/repro-clodo.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createEnhancedRouter } from '../src/routing/EnhancedRouter.js';
|
|
12
|
+
|
|
13
|
+
console.log('🧪 CLODO Framework Router API Compatibility Test\n');
|
|
14
|
+
console.log('=' .repeat(60));
|
|
15
|
+
|
|
16
|
+
// Mock D1 Client
|
|
17
|
+
const mockD1Client = {
|
|
18
|
+
prepare: () => ({ bind: () => ({}) }),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Create router instance
|
|
22
|
+
const router = createEnhancedRouter(mockD1Client, {
|
|
23
|
+
requireAuth: false,
|
|
24
|
+
allowPublicRead: true
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
console.log('\n📋 Router Instance Properties:');
|
|
28
|
+
console.log(' Keys:', Object.keys(router).sort());
|
|
29
|
+
console.log(' d1Client present:', !!router.d1Client);
|
|
30
|
+
console.log(' options present:', !!router.options);
|
|
31
|
+
console.log(' routes Map present:', router.routes instanceof Map);
|
|
32
|
+
console.log(' genericHandlers present:', !!router.genericHandlers);
|
|
33
|
+
|
|
34
|
+
console.log('\n📋 Express-like Methods Available:');
|
|
35
|
+
const expressLikeMethods = ['get', 'post', 'put', 'patch', 'delete'];
|
|
36
|
+
for (const method of expressLikeMethods) {
|
|
37
|
+
console.log(` typeof router.${method}:`, typeof router[method]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log('\n🔧 Testing Express-like API:');
|
|
41
|
+
|
|
42
|
+
// Test 1: router.get()
|
|
43
|
+
console.log('\n Test 1: router.get()');
|
|
44
|
+
try {
|
|
45
|
+
router.get('/api/users', (req) => new Response('GET /api/users'));
|
|
46
|
+
console.log(' ✅ router.get() registered successfully');
|
|
47
|
+
console.log(' ✓ Route key:', 'GET /api/users');
|
|
48
|
+
console.log(' ✓ Handler registered:', router.routes.has('GET /api/users'));
|
|
49
|
+
} catch (e) {
|
|
50
|
+
console.log(' ❌ router.get() failed:', e.message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Test 2: router.post()
|
|
54
|
+
console.log('\n Test 2: router.post()');
|
|
55
|
+
try {
|
|
56
|
+
router.post('/api/users', (req) => new Response('POST /api/users', { status: 201 }));
|
|
57
|
+
console.log(' ✅ router.post() registered successfully');
|
|
58
|
+
console.log(' ✓ Route key:', 'POST /api/users');
|
|
59
|
+
console.log(' ✓ Handler registered:', router.routes.has('POST /api/users'));
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.log(' ❌ router.post() failed:', e.message);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Test 3: router.get() with parameters
|
|
65
|
+
console.log('\n Test 3: router.get() with parameters');
|
|
66
|
+
try {
|
|
67
|
+
router.get('/api/users/:id', (req, id) => new Response(`GET /api/users/${id}`));
|
|
68
|
+
console.log(' ✅ router.get() with parameters registered successfully');
|
|
69
|
+
console.log(' ✓ Route key:', 'GET /api/users/:id');
|
|
70
|
+
console.log(' ✓ Handler registered:', router.routes.has('GET /api/users/:id'));
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.log(' ❌ router.get() with parameters failed:', e.message);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Test 4: router.patch()
|
|
76
|
+
console.log('\n Test 4: router.patch()');
|
|
77
|
+
try {
|
|
78
|
+
router.patch('/api/users/:id', (req, id) => new Response(`PATCH /api/users/${id}`));
|
|
79
|
+
console.log(' ✅ router.patch() registered successfully');
|
|
80
|
+
console.log(' ✓ Route key:', 'PATCH /api/users/:id');
|
|
81
|
+
console.log(' ✓ Handler registered:', router.routes.has('PATCH /api/users/:id'));
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.log(' ❌ router.patch() failed:', e.message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Test 5: router.delete()
|
|
87
|
+
console.log('\n Test 5: router.delete()');
|
|
88
|
+
try {
|
|
89
|
+
router.delete('/api/users/:id', (req, id) => new Response(`DELETE /api/users/${id}`));
|
|
90
|
+
console.log(' ✅ router.delete() registered successfully');
|
|
91
|
+
console.log(' ✓ Route key:', 'DELETE /api/users/:id');
|
|
92
|
+
console.log(' ✓ Handler registered:', router.routes.has('DELETE /api/users/:id'));
|
|
93
|
+
} catch (e) {
|
|
94
|
+
console.log(' ❌ router.delete() failed:', e.message);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('\n🔧 Testing Traditional registerRoute() API:');
|
|
98
|
+
|
|
99
|
+
// Test 6: registerRoute()
|
|
100
|
+
console.log('\n Test 6: registerRoute()');
|
|
101
|
+
try {
|
|
102
|
+
router.registerRoute('GET', '/health', (req) => new Response('OK'));
|
|
103
|
+
console.log(' ✅ registerRoute() works correctly');
|
|
104
|
+
console.log(' ✓ Route key:', 'GET /health');
|
|
105
|
+
console.log(' ✓ Handler registered:', router.routes.has('GET /health'));
|
|
106
|
+
} catch (e) {
|
|
107
|
+
console.log(' ❌ registerRoute() failed:', e.message);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log('\n📊 Route Summary:');
|
|
111
|
+
console.log(` Total routes registered: ${router.routes.size}`);
|
|
112
|
+
console.log(' Registered routes:');
|
|
113
|
+
for (const [key] of router.routes.entries()) {
|
|
114
|
+
console.log(` - ${key}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log('\n' + '='.repeat(60));
|
|
118
|
+
console.log('✨ All tests completed!');
|
|
119
|
+
console.log('\nConclusion:');
|
|
120
|
+
console.log(' ✅ Express-like methods (router.get, router.post, etc.) are now available');
|
|
121
|
+
console.log(' ✅ registerRoute() method still works');
|
|
122
|
+
console.log(' ✅ Both APIs are fully compatible and interchangeable');
|
|
123
|
+
console.log(' ✅ Framework is backward compatible with Express patterns');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SERVICE_NAME}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "wrangler dev",
|
|
8
|
+
"deploy": "wrangler deploy",
|
|
9
|
+
"test": "vitest"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@tamyla/clodo-framework": "^4.4.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"wrangler": "^3.0.0",
|
|
16
|
+
"vitest": "^2.0.0",
|
|
17
|
+
"@cloudflare/vitest-pool-workers": "^0.5.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Worker Template — @tamyla/clodo-framework
|
|
3
|
+
*
|
|
4
|
+
* A fully working Workers AI service with:
|
|
5
|
+
* - Text generation (chat/completion)
|
|
6
|
+
* - Streaming responses (SSE)
|
|
7
|
+
* - Embeddings generation
|
|
8
|
+
* - Prompt formatting
|
|
9
|
+
* - CORS + error handling + rate limiting
|
|
10
|
+
*
|
|
11
|
+
* Deploy with: npx wrangler deploy
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
createCorsMiddleware,
|
|
16
|
+
createErrorHandler,
|
|
17
|
+
createLogger,
|
|
18
|
+
createRateLimitGuard,
|
|
19
|
+
composeMiddleware,
|
|
20
|
+
createEnvironmentGuard
|
|
21
|
+
} from '@tamyla/clodo-framework';
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
AIClient,
|
|
25
|
+
Models,
|
|
26
|
+
createSSEStream,
|
|
27
|
+
streamResponse
|
|
28
|
+
} from '@tamyla/clodo-framework/utilities/ai';
|
|
29
|
+
|
|
30
|
+
import { formatAIPrompt } from '@tamyla/clodo-framework/utilities';
|
|
31
|
+
|
|
32
|
+
// ── Environment validation ────────────────────────────────────────────
|
|
33
|
+
const envGuard = createEnvironmentGuard({
|
|
34
|
+
required: ['AI'],
|
|
35
|
+
optional: ['KV_DATA', 'VECTORIZE_INDEX']
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ── Middleware stack ──────────────────────────────────────────────────
|
|
39
|
+
const middleware = composeMiddleware(
|
|
40
|
+
createCorsMiddleware({ origins: ['*'] }),
|
|
41
|
+
createLogger({ prefix: 'ai-worker', level: 'info' }),
|
|
42
|
+
createRateLimitGuard({ maxRequests: 50, windowMs: 60000 }),
|
|
43
|
+
createErrorHandler({ includeStack: false })
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// ── Routes ────────────────────────────────────────────────────────────
|
|
47
|
+
const routes = {
|
|
48
|
+
// Health check
|
|
49
|
+
'GET /health': async (req, env) => {
|
|
50
|
+
return jsonResponse({ status: 'healthy', model: Models.CHAT });
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
// Chat completion (non-streaming)
|
|
54
|
+
'POST /api/chat': async (req, env) => {
|
|
55
|
+
const { messages, model, max_tokens } = await req.json();
|
|
56
|
+
const ai = new AIClient(env.AI);
|
|
57
|
+
|
|
58
|
+
const response = await ai.chat(messages || [], {
|
|
59
|
+
model: model || Models.CHAT,
|
|
60
|
+
max_tokens: max_tokens || 1024
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return jsonResponse({ response: response.response, model: model || Models.CHAT });
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Chat completion (streaming SSE)
|
|
67
|
+
'POST /api/chat/stream': async (req, env) => {
|
|
68
|
+
const { messages, model, max_tokens } = await req.json();
|
|
69
|
+
const ai = new AIClient(env.AI);
|
|
70
|
+
|
|
71
|
+
const stream = await ai.run(model || Models.CHAT, {
|
|
72
|
+
messages: messages || [],
|
|
73
|
+
max_tokens: max_tokens || 1024,
|
|
74
|
+
stream: true
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return streamResponse(stream);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Text generation from prompt template
|
|
81
|
+
'POST /api/generate': async (req, env) => {
|
|
82
|
+
const { prompt, context, model, max_tokens } = await req.json();
|
|
83
|
+
const ai = new AIClient(env.AI);
|
|
84
|
+
|
|
85
|
+
// Format the prompt with context variables
|
|
86
|
+
const formattedPrompt = context ? formatAIPrompt(prompt, context) : prompt;
|
|
87
|
+
|
|
88
|
+
const response = await ai.generate(formattedPrompt, {
|
|
89
|
+
model: model || Models.TEXT_GENERATION,
|
|
90
|
+
max_tokens: max_tokens || 2048
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return jsonResponse({ response: response.response, prompt: formattedPrompt });
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Generate embeddings
|
|
97
|
+
'POST /api/embeddings': async (req, env) => {
|
|
98
|
+
const { text, texts, model } = await req.json();
|
|
99
|
+
const ai = new AIClient(env.AI);
|
|
100
|
+
|
|
101
|
+
const input = texts || [text];
|
|
102
|
+
const embeddings = await ai.embed(input, {
|
|
103
|
+
model: model || Models.EMBEDDINGS
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return jsonResponse({
|
|
107
|
+
embeddings: embeddings.data,
|
|
108
|
+
model: model || Models.EMBEDDINGS,
|
|
109
|
+
dimensions: embeddings.data?.[0]?.length || 0
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// Summarize text
|
|
114
|
+
'POST /api/summarize': async (req, env) => {
|
|
115
|
+
const { text, max_length } = await req.json();
|
|
116
|
+
const ai = new AIClient(env.AI);
|
|
117
|
+
|
|
118
|
+
const result = await ai.summarize(text, {
|
|
119
|
+
max_length: max_length || 256
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return jsonResponse({ summary: result.summary });
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// List available models
|
|
126
|
+
'GET /api/models': async (req, env) => {
|
|
127
|
+
return jsonResponse({
|
|
128
|
+
models: Object.entries(Models).map(([key, value]) => ({
|
|
129
|
+
name: key,
|
|
130
|
+
model: value
|
|
131
|
+
}))
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
137
|
+
function jsonResponse(data, status = 200) {
|
|
138
|
+
return new Response(JSON.stringify(data), {
|
|
139
|
+
status,
|
|
140
|
+
headers: { 'Content-Type': 'application/json' }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Worker entry point ────────────────────────────────────────────────
|
|
145
|
+
export default {
|
|
146
|
+
async fetch(request, env, ctx) {
|
|
147
|
+
envGuard.check(env);
|
|
148
|
+
|
|
149
|
+
const url = new URL(request.url);
|
|
150
|
+
const routeKey = `${request.method} ${url.pathname}`;
|
|
151
|
+
const handler = routes[routeKey];
|
|
152
|
+
|
|
153
|
+
if (!handler) {
|
|
154
|
+
return jsonResponse({ error: 'Not Found', path: url.pathname }, 404);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Execute through middleware chain
|
|
158
|
+
return middleware.execute(request, () => handler(request, env, ctx));
|
|
159
|
+
}
|
|
160
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{SERVICE_NAME}}",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "wrangler dev",
|
|
8
|
+
"deploy": "wrangler deploy",
|
|
9
|
+
"test": "vitest"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@tamyla/clodo-framework": "^4.4.1"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"wrangler": "^3.0.0",
|
|
16
|
+
"vitest": "^2.0.0",
|
|
17
|
+
"@cloudflare/vitest-pool-workers": "^0.5.0"
|
|
18
|
+
}
|
|
19
|
+
}
|