services-as-software 2.0.2 → 2.1.1

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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # services-as-software
2
2
 
3
+ ## 2.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [6beb531]
8
+ - ai-functions@2.1.1
9
+ - ai-database@2.1.1
10
+ - digital-workers@2.1.1
11
+
12
+ ## 2.0.3
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies
17
+ - rpc.do@0.2.0
18
+ - ai-database@2.0.3
19
+ - ai-functions@2.0.3
20
+ - digital-workers@2.0.3
21
+
3
22
  ## 2.0.2
4
23
 
5
24
  ### Patch Changes
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Example: Using the Service Client
3
+ *
4
+ * This example demonstrates how to connect to a remote service using the Client
5
+ */
6
+ import { Client, Provider, providers } from '../src/index.js';
7
+ // Connect to a service using a direct URL
8
+ const translationClient = Client({
9
+ url: 'https://api.example.com/translation',
10
+ auth: {
11
+ type: 'api-key',
12
+ credentials: {
13
+ apiKey: 'your-api-key-here',
14
+ },
15
+ },
16
+ });
17
+ // Use the client
18
+ async function useDirectClient() {
19
+ const result = await translationClient.do('translate', {
20
+ text: 'Hello, world!',
21
+ to: 'es',
22
+ });
23
+ console.log('Translation:', result);
24
+ // Get a quote
25
+ const quote = await translationClient.quote({ text: 'Large document...', to: 'es' }, 1000);
26
+ console.log('Quote:', quote);
27
+ // Subscribe to a plan
28
+ const subscription = await translationClient.subscribe('pro');
29
+ console.log('Subscription:', subscription);
30
+ }
31
+ // Use a provider to access multiple services
32
+ const awsProvider = providers.aws({
33
+ accessKeyId: 'your-access-key',
34
+ secretAccessKey: 'your-secret-key',
35
+ region: 'us-east-1',
36
+ });
37
+ async function useProvider() {
38
+ // Get the translate service from AWS
39
+ const translate = awsProvider.service('translate');
40
+ const result = await translate.do('translate', {
41
+ text: 'Hello, world!',
42
+ sourceLang: 'en',
43
+ targetLang: 'es',
44
+ });
45
+ console.log('AWS Translation:', result);
46
+ }
47
+ // Create a custom provider
48
+ const customProvider = Provider({
49
+ name: 'My Company',
50
+ baseUrl: 'https://api.mycompany.com',
51
+ auth: {
52
+ type: 'jwt',
53
+ credentials: {
54
+ token: 'your-jwt-token',
55
+ },
56
+ },
57
+ services: ['translation', 'summarization', 'sentiment'],
58
+ });
59
+ async function useCustomProvider() {
60
+ const translation = customProvider.service('translation');
61
+ const result = await translation.do('translate', {
62
+ text: 'Hello',
63
+ to: 'fr',
64
+ });
65
+ console.log('Custom provider result:', result);
66
+ }
67
+ // Export examples
68
+ export { useDirectClient, useProvider, useCustomProvider };
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Example: Translation Service
3
+ *
4
+ * This example demonstrates how to create a translation service with:
5
+ * - Multiple endpoints (translate, detect language)
6
+ * - Per-use pricing
7
+ * - Rate limiting
8
+ * - Usage tracking
9
+ */
10
+ import { Service, Endpoint, POST, GET } from '../src/index.js';
11
+ // Define the translation service
12
+ const translationService = Service({
13
+ name: 'translation-service',
14
+ version: '1.0.0',
15
+ description: 'AI-powered translation service',
16
+ status: 'active',
17
+ // Default pricing for all endpoints
18
+ pricing: {
19
+ model: 'per-use',
20
+ pricePerUnit: 0.01, // $0.01 per character
21
+ currency: 'USD',
22
+ freeTier: {
23
+ units: 10000, // 10,000 free characters per month
24
+ resetInterval: 'monthly',
25
+ },
26
+ },
27
+ // Service endpoints
28
+ endpoints: [
29
+ POST({
30
+ name: 'translate',
31
+ description: 'Translate text between languages',
32
+ path: '/translate',
33
+ input: {
34
+ type: 'object',
35
+ properties: {
36
+ text: { type: 'string', description: 'Text to translate' },
37
+ from: { type: 'string', description: 'Source language code (optional)' },
38
+ to: { type: 'string', description: 'Target language code' },
39
+ },
40
+ required: ['text', 'to'],
41
+ additionalProperties: false,
42
+ },
43
+ output: {
44
+ type: 'object',
45
+ properties: {
46
+ translatedText: { type: 'string', description: 'Translated text' },
47
+ sourceLanguage: { type: 'string', description: 'Detected or specified source language' },
48
+ confidence: { type: 'number', description: 'Translation confidence (0-1)' },
49
+ },
50
+ required: ['translatedText', 'sourceLanguage', 'confidence'],
51
+ additionalProperties: false,
52
+ },
53
+ handler: async (input, context) => {
54
+ // Track usage for billing
55
+ if (context?.usage && context.customerId) {
56
+ await context.usage.track({
57
+ customerId: context.customerId,
58
+ resource: 'translate',
59
+ quantity: input.text.length, // Charge per character
60
+ timestamp: new Date(),
61
+ });
62
+ }
63
+ // In a real implementation, this would call an AI translation API
64
+ return {
65
+ translatedText: `[Translated to ${input.to}] ${input.text}`,
66
+ sourceLanguage: input.from || 'auto-detected',
67
+ confidence: 0.95,
68
+ };
69
+ },
70
+ rateLimit: {
71
+ requests: 100,
72
+ window: 60000, // 100 requests per minute
73
+ },
74
+ }),
75
+ GET({
76
+ name: 'detect',
77
+ description: 'Detect the language of text',
78
+ path: '/detect',
79
+ input: {
80
+ type: 'object',
81
+ properties: {
82
+ text: { type: 'string', description: 'Text to analyze' },
83
+ },
84
+ required: ['text'],
85
+ additionalProperties: false,
86
+ },
87
+ output: {
88
+ type: 'object',
89
+ properties: {
90
+ language: { type: 'string', description: 'Detected language code' },
91
+ confidence: { type: 'number', description: 'Detection confidence (0-1)' },
92
+ },
93
+ required: ['language', 'confidence'],
94
+ additionalProperties: false,
95
+ },
96
+ handler: async (input) => {
97
+ // Language detection logic
98
+ return {
99
+ language: 'en',
100
+ confidence: 0.98,
101
+ };
102
+ },
103
+ pricing: {
104
+ model: 'per-use',
105
+ pricePerUnit: 0.001, // $0.001 per detection (cheaper than translation)
106
+ currency: 'USD',
107
+ },
108
+ }),
109
+ Endpoint({
110
+ name: 'languages',
111
+ description: 'Get supported languages',
112
+ method: 'GET',
113
+ path: '/languages',
114
+ handler: async () => {
115
+ return {
116
+ languages: [
117
+ { code: 'en', name: 'English' },
118
+ { code: 'es', name: 'Spanish' },
119
+ { code: 'fr', name: 'French' },
120
+ { code: 'de', name: 'German' },
121
+ { code: 'ja', name: 'Japanese' },
122
+ { code: 'zh', name: 'Chinese' },
123
+ ],
124
+ };
125
+ },
126
+ requiresAuth: false, // Public endpoint
127
+ }),
128
+ ],
129
+ // Subscription plans
130
+ plans: [
131
+ {
132
+ id: 'starter',
133
+ name: 'Starter Plan',
134
+ description: 'For individual developers',
135
+ pricing: {
136
+ model: 'subscription',
137
+ basePrice: 9.99,
138
+ currency: 'USD',
139
+ interval: 'monthly',
140
+ },
141
+ entitlements: ['api-access', 'basic-support'],
142
+ features: [
143
+ '100,000 characters/month',
144
+ 'Email support',
145
+ 'Basic languages',
146
+ ],
147
+ limits: {
148
+ characters: 100000,
149
+ },
150
+ },
151
+ {
152
+ id: 'pro',
153
+ name: 'Pro Plan',
154
+ description: 'For growing businesses',
155
+ pricing: {
156
+ model: 'subscription',
157
+ basePrice: 49.99,
158
+ currency: 'USD',
159
+ interval: 'monthly',
160
+ },
161
+ entitlements: ['api-access', 'priority-support', 'advanced-features'],
162
+ features: [
163
+ '1,000,000 characters/month',
164
+ 'Priority support',
165
+ 'All languages',
166
+ 'Custom models',
167
+ ],
168
+ limits: {
169
+ characters: 1000000,
170
+ },
171
+ },
172
+ ],
173
+ // KPIs
174
+ kpis: [
175
+ {
176
+ id: 'translations-per-day',
177
+ name: 'Translations per Day',
178
+ description: 'Number of translation requests processed daily',
179
+ calculate: async () => {
180
+ // In a real implementation, query the database
181
+ return 1247;
182
+ },
183
+ target: 1000,
184
+ unit: 'requests',
185
+ },
186
+ {
187
+ id: 'average-confidence',
188
+ name: 'Average Translation Confidence',
189
+ description: 'Average confidence score across all translations',
190
+ calculate: async () => {
191
+ return 0.94;
192
+ },
193
+ target: 0.95,
194
+ unit: 'score',
195
+ },
196
+ ],
197
+ });
198
+ // Example usage
199
+ async function example() {
200
+ // Call the translate endpoint
201
+ const result = await translationService.call('translate', {
202
+ text: 'Hello, world!',
203
+ to: 'es',
204
+ });
205
+ console.log('Translation result:', result);
206
+ // Get KPIs
207
+ const kpis = await translationService.kpis();
208
+ console.log('KPIs:', kpis);
209
+ // Subscribe to a plan
210
+ const subscription = await translationService.subscribe('pro');
211
+ console.log('Subscription:', subscription);
212
+ }
213
+ // Export the service
214
+ export { translationService };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "services-as-software",
3
- "version": "2.0.2",
3
+ "version": "2.1.1",
4
4
  "description": "Primitives for building AI-powered services that operate as software",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,10 +20,9 @@
20
20
  "clean": "rm -rf dist"
21
21
  },
22
22
  "dependencies": {
23
- "ai-database": "2.0.2",
24
- "ai-functions": "2.0.2",
25
- "digital-workers": "2.0.2",
26
- "rpc.do": "^0.1.0"
23
+ "ai-database": "2.1.1",
24
+ "ai-functions": "2.1.1",
25
+ "digital-workers": "2.1.1"
27
26
  },
28
27
  "keywords": [
29
28
  "services",
package/src/client.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Service client for connecting to remote services
3
+ */
4
+ /**
5
+ * Create a client to connect to a remote service
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const client = Client({
10
+ * url: 'https://api.example.com/translation',
11
+ * auth: {
12
+ * type: 'api-key',
13
+ * credentials: { apiKey: process.env.API_KEY },
14
+ * },
15
+ * })
16
+ *
17
+ * const result = await client.do('translate', {
18
+ * text: 'Hello world',
19
+ * to: 'es',
20
+ * })
21
+ * ```
22
+ */
23
+ export function Client(config) {
24
+ const baseUrl = config.url || '';
25
+ const headers = {
26
+ 'Content-Type': 'application/json',
27
+ ...config.headers,
28
+ };
29
+ // Add auth headers
30
+ if (config.auth) {
31
+ switch (config.auth.type) {
32
+ case 'api-key':
33
+ headers['Authorization'] = `Bearer ${config.auth.credentials.apiKey || config.auth.credentials.token}`;
34
+ break;
35
+ case 'basic':
36
+ const basicAuth = Buffer.from(`${config.auth.credentials.username}:${config.auth.credentials.password}`).toString('base64');
37
+ headers['Authorization'] = `Basic ${basicAuth}`;
38
+ break;
39
+ case 'jwt':
40
+ headers['Authorization'] = `Bearer ${config.auth.credentials.token}`;
41
+ break;
42
+ // OAuth would require more complex flow
43
+ }
44
+ }
45
+ /**
46
+ * Make an HTTP request to the service
47
+ */
48
+ async function request(endpoint, body) {
49
+ const url = `${baseUrl}/${endpoint.replace(/^\//, '')}`;
50
+ const response = await fetch(url, {
51
+ method: 'POST',
52
+ headers,
53
+ body: body ? JSON.stringify(body) : undefined,
54
+ signal: config.timeout ? AbortSignal.timeout(config.timeout) : undefined,
55
+ });
56
+ if (!response.ok) {
57
+ throw new Error(`Service request failed: ${response.status} ${response.statusText}`);
58
+ }
59
+ return response.json();
60
+ }
61
+ return {
62
+ async ask(question, context) {
63
+ const result = await request('ask', { question, context });
64
+ return result.answer;
65
+ },
66
+ async deliver(orderId, results) {
67
+ await request('deliver', { orderId, results });
68
+ },
69
+ async do(action, input) {
70
+ return request('do', { action, input });
71
+ },
72
+ async generate(prompt, options) {
73
+ return request('generate', { prompt, options });
74
+ },
75
+ async is(value, type) {
76
+ const result = await request('is', { value, type });
77
+ return result.result;
78
+ },
79
+ async notify(notification) {
80
+ await request('notify', notification);
81
+ },
82
+ async order(product, quantity) {
83
+ return request('order', { product, quantity });
84
+ },
85
+ async quote(product, quantity) {
86
+ return request('quote', { product, quantity });
87
+ },
88
+ async subscribe(planId) {
89
+ return request('subscribe', { planId });
90
+ },
91
+ async entitlements() {
92
+ const result = await request('entitlements', {});
93
+ return result.entitlements;
94
+ },
95
+ async kpis() {
96
+ return request('kpis', {});
97
+ },
98
+ async okrs() {
99
+ return request('okrs', {});
100
+ },
101
+ };
102
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Endpoint helper for defining service endpoints
3
+ */
4
+ /**
5
+ * Create an endpoint definition
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const translateEndpoint = Endpoint({
10
+ * name: 'translate',
11
+ * description: 'Translate text between languages',
12
+ * method: 'POST',
13
+ * path: '/translate',
14
+ * input: {
15
+ * type: 'object',
16
+ * properties: {
17
+ * text: { type: 'string', description: 'Text to translate' },
18
+ * from: { type: 'string', description: 'Source language code' },
19
+ * to: { type: 'string', description: 'Target language code' },
20
+ * },
21
+ * required: ['text', 'to'],
22
+ * additionalProperties: false,
23
+ * },
24
+ * output: {
25
+ * type: 'object',
26
+ * properties: {
27
+ * translatedText: { type: 'string' },
28
+ * confidence: { type: 'number' },
29
+ * },
30
+ * required: ['translatedText'],
31
+ * additionalProperties: false,
32
+ * },
33
+ * handler: async (input, context) => {
34
+ * // Translation logic here
35
+ * return {
36
+ * translatedText: `Translated: ${input.text}`,
37
+ * confidence: 0.95,
38
+ * }
39
+ * },
40
+ * pricing: {
41
+ * model: 'per-use',
42
+ * pricePerUnit: 0.01,
43
+ * currency: 'USD',
44
+ * },
45
+ * rateLimit: {
46
+ * requests: 100,
47
+ * window: 60000, // 1 minute
48
+ * },
49
+ * })
50
+ * ```
51
+ */
52
+ export function Endpoint(config) {
53
+ return {
54
+ name: config.name,
55
+ description: config.description,
56
+ method: config.method || 'POST',
57
+ path: config.path || `/${config.name}`,
58
+ input: config.input,
59
+ output: config.output,
60
+ handler: config.handler,
61
+ pricing: config.pricing,
62
+ rateLimit: config.rateLimit,
63
+ requiresAuth: config.requiresAuth !== false, // Default to true
64
+ };
65
+ }
66
+ /**
67
+ * Create a GET endpoint
68
+ */
69
+ export function GET(config) {
70
+ return Endpoint({ ...config, method: 'GET' });
71
+ }
72
+ /**
73
+ * Create a POST endpoint
74
+ */
75
+ export function POST(config) {
76
+ return Endpoint({ ...config, method: 'POST' });
77
+ }
78
+ /**
79
+ * Create a PUT endpoint
80
+ */
81
+ export function PUT(config) {
82
+ return Endpoint({ ...config, method: 'PUT' });
83
+ }
84
+ /**
85
+ * Create a DELETE endpoint
86
+ */
87
+ export function DELETE(config) {
88
+ return Endpoint({ ...config, method: 'DELETE' });
89
+ }
90
+ /**
91
+ * Create a PATCH endpoint
92
+ */
93
+ export function PATCH(config) {
94
+ return Endpoint({ ...config, method: 'PATCH' });
95
+ }