@dxos/functions 0.8.4-main.7ace549 → 0.8.4-main.8360d9e660
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 +4 -6
- package/dist/lib/{browser → neutral}/index.mjs +327 -114
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/errors.d.ts +24 -32
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/example/index.d.ts +5 -5
- package/dist/types/src/example/index.d.ts.map +1 -1
- package/dist/types/src/operation-compatibility.test.d.ts +2 -0
- package/dist/types/src/operation-compatibility.test.d.ts.map +1 -0
- package/dist/types/src/protocol/functions-ai-http-client.d.ts +12 -0
- package/dist/types/src/protocol/functions-ai-http-client.d.ts.map +1 -0
- package/dist/types/src/protocol/protocol.d.ts.map +1 -1
- package/dist/types/src/sdk.d.ts +26 -9
- package/dist/types/src/sdk.d.ts.map +1 -1
- package/dist/types/src/services/credentials.d.ts +6 -4
- package/dist/types/src/services/credentials.d.ts.map +1 -1
- package/dist/types/src/services/event-logger.d.ts +25 -31
- package/dist/types/src/services/event-logger.d.ts.map +1 -1
- package/dist/types/src/services/function-invocation-service.d.ts +5 -0
- package/dist/types/src/services/function-invocation-service.d.ts.map +1 -1
- package/dist/types/src/services/index.d.ts +0 -1
- package/dist/types/src/services/index.d.ts.map +1 -1
- package/dist/types/src/services/tracing.d.ts +37 -3
- package/dist/types/src/services/tracing.d.ts.map +1 -1
- package/dist/types/src/types/Function.d.ts +34 -45
- package/dist/types/src/types/Function.d.ts.map +1 -1
- package/dist/types/src/types/Script.d.ts +8 -15
- package/dist/types/src/types/Script.d.ts.map +1 -1
- package/dist/types/src/types/Trigger.d.ts +59 -78
- package/dist/types/src/types/Trigger.d.ts.map +1 -1
- package/dist/types/src/types/TriggerEvent.d.ts +44 -12
- package/dist/types/src/types/TriggerEvent.d.ts.map +1 -1
- package/dist/types/src/types/url.d.ts +6 -5
- package/dist/types/src/types/url.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -18
- package/src/errors.ts +4 -4
- package/src/example/forex-effect.ts +1 -1
- package/src/example/index.ts +8 -8
- package/src/operation-compatibility.test.ts +185 -0
- package/src/protocol/functions-ai-http-client.ts +67 -0
- package/src/protocol/protocol.ts +117 -15
- package/src/sdk.ts +73 -16
- package/src/services/credentials.ts +31 -15
- package/src/services/event-logger.ts +2 -2
- package/src/services/function-invocation-service.ts +14 -0
- package/src/services/index.ts +0 -2
- package/src/services/tracing.ts +63 -4
- package/src/types/Function.ts +16 -13
- package/src/types/Script.ts +9 -4
- package/src/types/Trigger.ts +30 -16
- package/src/types/TriggerEvent.ts +29 -29
- package/src/types/url.ts +6 -5
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -1031
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/src/sdk.ts
CHANGED
|
@@ -7,9 +7,10 @@ import * as Effect from 'effect/Effect';
|
|
|
7
7
|
import * as Schema from 'effect/Schema';
|
|
8
8
|
|
|
9
9
|
import { type AiService } from '@dxos/ai';
|
|
10
|
-
import { Obj, Type } from '@dxos/echo';
|
|
11
|
-
import { type
|
|
10
|
+
import { type Feed, JsonSchema, Obj, type Type } from '@dxos/echo';
|
|
11
|
+
import { type Database } from '@dxos/echo';
|
|
12
12
|
import { assertArgument, failedInvariant } from '@dxos/invariant';
|
|
13
|
+
import { Operation } from '@dxos/operation';
|
|
13
14
|
|
|
14
15
|
import {
|
|
15
16
|
type CredentialsService,
|
|
@@ -37,8 +38,10 @@ export type FunctionServices =
|
|
|
37
38
|
| InvocationServices
|
|
38
39
|
| AiService.AiService
|
|
39
40
|
| CredentialsService
|
|
40
|
-
|
|
|
41
|
+
| Database.Service
|
|
42
|
+
// TODO(wittjosiah): Remove QueueService — use Feed.Service instead.
|
|
41
43
|
| QueueService
|
|
44
|
+
| Feed.Service
|
|
42
45
|
| FunctionInvocationService;
|
|
43
46
|
|
|
44
47
|
/**
|
|
@@ -67,26 +70,29 @@ export interface FunctionContext {
|
|
|
67
70
|
|
|
68
71
|
const typeId = Symbol.for('@dxos/functions/FunctionDefinition');
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Deployable function definition.
|
|
75
|
+
*/
|
|
76
|
+
export type FunctionDefinition<TInput = any, TOutput = any, S extends FunctionServices = FunctionServices> = {
|
|
71
77
|
[typeId]: true;
|
|
78
|
+
|
|
72
79
|
key: string;
|
|
73
80
|
name: string;
|
|
74
81
|
description?: string;
|
|
75
|
-
inputSchema: Schema.Schema<
|
|
76
|
-
outputSchema?: Schema.Schema<
|
|
82
|
+
inputSchema: Schema.Schema<TInput, any>;
|
|
83
|
+
outputSchema?: Schema.Schema<TOutput, any>;
|
|
77
84
|
|
|
78
85
|
/**
|
|
79
86
|
* List of types the function uses.
|
|
80
87
|
* This is used to ensure that the types are available when the function is executed.
|
|
81
88
|
*/
|
|
82
|
-
types: readonly Type.
|
|
89
|
+
types: readonly Type.AnyEntity[];
|
|
83
90
|
|
|
84
91
|
/**
|
|
85
92
|
* Keys of the required services.
|
|
86
93
|
*/
|
|
87
94
|
services: readonly string[];
|
|
88
95
|
|
|
89
|
-
handler: FunctionHandler<T, O, S>;
|
|
90
96
|
meta?: {
|
|
91
97
|
/**
|
|
92
98
|
* Tools that are projected from functions have this annotation.
|
|
@@ -99,6 +105,8 @@ export type FunctionDefinition<T = any, O = any, S extends FunctionServices = Fu
|
|
|
99
105
|
*/
|
|
100
106
|
deployedFunctionId?: string;
|
|
101
107
|
};
|
|
108
|
+
|
|
109
|
+
handler: FunctionHandler<TInput, TOutput, S>;
|
|
102
110
|
};
|
|
103
111
|
|
|
104
112
|
export declare namespace FunctionDefinition {
|
|
@@ -119,7 +127,8 @@ export type FunctionProps<T, O> = {
|
|
|
119
127
|
* List of types the function uses.
|
|
120
128
|
* This is used to ensure that the types are available when the function is executed.
|
|
121
129
|
*/
|
|
122
|
-
types?: readonly Type.
|
|
130
|
+
types?: readonly Type.AnyEntity[];
|
|
131
|
+
|
|
123
132
|
// TODO(dmaretskyi): This currently doesn't cause a compile-time error if the handler requests a service that is not specified
|
|
124
133
|
services?: readonly Context.Tag<any, any>[];
|
|
125
134
|
|
|
@@ -176,7 +185,7 @@ export const defineFunction: {
|
|
|
176
185
|
handler: handlerWithSpan,
|
|
177
186
|
types: types ?? [],
|
|
178
187
|
services: !services ? [] : getServiceKeys(services),
|
|
179
|
-
}
|
|
188
|
+
};
|
|
180
189
|
};
|
|
181
190
|
|
|
182
191
|
const getServiceKeys = (services: readonly Context.Tag<any, any>[]) => {
|
|
@@ -184,11 +193,58 @@ const getServiceKeys = (services: readonly Context.Tag<any, any>[]) => {
|
|
|
184
193
|
if (typeof tag.key === 'string') {
|
|
185
194
|
return tag.key;
|
|
186
195
|
}
|
|
187
|
-
console.log(tag);
|
|
188
196
|
failedInvariant();
|
|
189
197
|
});
|
|
190
198
|
};
|
|
191
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Converts a FunctionDefinition to an OperationDefinition with handler.
|
|
202
|
+
* The function handler is adapted to the OperationHandler format.
|
|
203
|
+
*
|
|
204
|
+
* Note: FunctionDefinition stores service keys as strings, not Tag types,
|
|
205
|
+
* so we can't use Operation.withHandler's type inference here.
|
|
206
|
+
*/
|
|
207
|
+
export const toOperation = <T, O, S extends FunctionServices = FunctionServices>(
|
|
208
|
+
functionDef: FunctionDefinition<T, O, S>,
|
|
209
|
+
): Operation.Definition<T, O> & { handler: Operation.Handler<T, O, any, S> } => {
|
|
210
|
+
const op = Operation.make({
|
|
211
|
+
schema: {
|
|
212
|
+
input: functionDef.inputSchema,
|
|
213
|
+
output: functionDef.outputSchema ?? Schema.Any,
|
|
214
|
+
},
|
|
215
|
+
meta: {
|
|
216
|
+
key: functionDef.key,
|
|
217
|
+
name: functionDef.name,
|
|
218
|
+
description: functionDef.description,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Adapt FunctionHandler signature to OperationHandler format.
|
|
223
|
+
// FunctionHandler expects { context, data }, OperationHandler expects just input.
|
|
224
|
+
const operationHandler: Operation.Handler<T, O, any, S> = (input: T) => {
|
|
225
|
+
const result = functionDef.handler({
|
|
226
|
+
context: {} as FunctionContext,
|
|
227
|
+
data: input,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Convert Promise or plain value to Effect.
|
|
231
|
+
if (Effect.isEffect(result)) {
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
if (result instanceof Promise) {
|
|
235
|
+
return Effect.tryPromise(() => result);
|
|
236
|
+
}
|
|
237
|
+
return Effect.succeed(result as O);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Manually attach handler since FunctionDefinition stores service keys as strings,
|
|
241
|
+
// not Tag types, so withHandler's type inference doesn't apply.
|
|
242
|
+
return {
|
|
243
|
+
...op,
|
|
244
|
+
handler: operationHandler,
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
|
|
192
248
|
export const FunctionDefinition = {
|
|
193
249
|
make: defineFunction,
|
|
194
250
|
isFunction: (value: unknown): value is FunctionDefinition.Any => {
|
|
@@ -202,6 +258,7 @@ export const FunctionDefinition = {
|
|
|
202
258
|
assertArgument(Obj.instanceOf(Function.Function, functionObj), 'functionObj');
|
|
203
259
|
return deserializeFunction(functionObj);
|
|
204
260
|
},
|
|
261
|
+
toOperation,
|
|
205
262
|
};
|
|
206
263
|
|
|
207
264
|
export const serializeFunction = (functionDef: FunctionDefinition.Any): Function.Function => {
|
|
@@ -210,12 +267,12 @@ export const serializeFunction = (functionDef: FunctionDefinition.Any): Function
|
|
|
210
267
|
name: functionDef.name,
|
|
211
268
|
version: '0.1.0',
|
|
212
269
|
description: functionDef.description,
|
|
213
|
-
inputSchema:
|
|
214
|
-
outputSchema: !functionDef.outputSchema ? undefined :
|
|
270
|
+
inputSchema: JsonSchema.toJsonSchema(functionDef.inputSchema),
|
|
271
|
+
outputSchema: !functionDef.outputSchema ? undefined : JsonSchema.toJsonSchema(functionDef.outputSchema),
|
|
215
272
|
services: functionDef.services,
|
|
216
273
|
});
|
|
217
274
|
if (functionDef.meta?.deployedFunctionId) {
|
|
218
|
-
setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta
|
|
275
|
+
Obj.change(fn, (fn) => setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta!.deployedFunctionId!));
|
|
219
276
|
}
|
|
220
277
|
return fn;
|
|
221
278
|
};
|
|
@@ -227,8 +284,8 @@ export const deserializeFunction = (functionObj: Function.Function): FunctionDef
|
|
|
227
284
|
key: functionObj.key ?? functionObj.name,
|
|
228
285
|
name: functionObj.name,
|
|
229
286
|
description: functionObj.description,
|
|
230
|
-
inputSchema: !functionObj.inputSchema ? Schema.Unknown :
|
|
231
|
-
outputSchema: !functionObj.outputSchema ? undefined :
|
|
287
|
+
inputSchema: !functionObj.inputSchema ? Schema.Unknown : JsonSchema.toEffectSchema(functionObj.inputSchema),
|
|
288
|
+
outputSchema: !functionObj.outputSchema ? undefined : JsonSchema.toEffectSchema(functionObj.outputSchema),
|
|
232
289
|
// TODO(dmaretskyi): This should throw error.
|
|
233
290
|
handler: () => {},
|
|
234
291
|
services: functionObj.services ?? [],
|
|
@@ -11,7 +11,7 @@ import * as Layer from 'effect/Layer';
|
|
|
11
11
|
import * as Redacted from 'effect/Redacted';
|
|
12
12
|
|
|
13
13
|
import { Query } from '@dxos/echo';
|
|
14
|
-
import {
|
|
14
|
+
import { Database } from '@dxos/echo';
|
|
15
15
|
import { AccessToken } from '@dxos/types';
|
|
16
16
|
|
|
17
17
|
export type CredentialQuery = {
|
|
@@ -60,7 +60,12 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
|
|
|
60
60
|
static configuredLayer = (credentials: ServiceCredential[]) =>
|
|
61
61
|
Layer.succeed(CredentialsService, new ConfiguredCredentialsService(credentials));
|
|
62
62
|
|
|
63
|
-
static layerConfig = (
|
|
63
|
+
static layerConfig = (
|
|
64
|
+
credentials: {
|
|
65
|
+
service: string;
|
|
66
|
+
apiKey: Config.Config<Redacted.Redacted<string>>;
|
|
67
|
+
}[],
|
|
68
|
+
) =>
|
|
64
69
|
Layer.effect(
|
|
65
70
|
CredentialsService,
|
|
66
71
|
Effect.gen(function* () {
|
|
@@ -77,20 +82,34 @@ export class CredentialsService extends Context.Tag('@dxos/functions/Credentials
|
|
|
77
82
|
}),
|
|
78
83
|
);
|
|
79
84
|
|
|
80
|
-
static layerFromDatabase = () =>
|
|
85
|
+
static layerFromDatabase = ({ caching = false }: { caching?: boolean } = {}) =>
|
|
81
86
|
Layer.effect(
|
|
82
87
|
CredentialsService,
|
|
83
88
|
Effect.gen(function* () {
|
|
84
|
-
const dbService = yield*
|
|
89
|
+
const dbService = yield* Database.Service;
|
|
90
|
+
const cache = new Map<string, ServiceCredential[]>();
|
|
91
|
+
|
|
85
92
|
const queryCredentials = async (query: CredentialQuery): Promise<ServiceCredential[]> => {
|
|
86
|
-
const
|
|
87
|
-
|
|
93
|
+
const cacheKey = JSON.stringify(query);
|
|
94
|
+
if (caching && cache.has(cacheKey)) {
|
|
95
|
+
return cache.get(cacheKey)!;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const accessTokens = await dbService.db.query(Query.type(AccessToken.AccessToken)).run();
|
|
99
|
+
const credentials = accessTokens
|
|
88
100
|
.filter((accessToken) => accessToken.source === query.service)
|
|
89
101
|
.map((accessToken) => ({
|
|
90
102
|
service: accessToken.source,
|
|
91
103
|
apiKey: accessToken.token,
|
|
92
104
|
}));
|
|
105
|
+
|
|
106
|
+
if (caching) {
|
|
107
|
+
cache.set(cacheKey, credentials);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return credentials;
|
|
93
111
|
};
|
|
112
|
+
|
|
94
113
|
return {
|
|
95
114
|
getCredential: async (query) => {
|
|
96
115
|
const credentials = await queryCredentials(query);
|
|
@@ -131,13 +150,10 @@ export class ConfiguredCredentialsService implements Context.Tag.Service<Credent
|
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
/**
|
|
134
|
-
* Maps the request to include the
|
|
153
|
+
* Maps the request to include the given token in the Authorization header.
|
|
135
154
|
*/
|
|
136
|
-
export const withAuthorization = (
|
|
137
|
-
HttpClient.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return HttpClientRequest.setHeader(request, 'Authorization', authorization);
|
|
142
|
-
}),
|
|
143
|
-
);
|
|
155
|
+
export const withAuthorization = (token: string, kind?: 'Bearer' | 'Basic') =>
|
|
156
|
+
HttpClient.mapRequest((request) => {
|
|
157
|
+
const authorization = kind ? `${kind} ${token}` : token;
|
|
158
|
+
return HttpClientRequest.setHeader(request, 'Authorization', authorization);
|
|
159
|
+
});
|
|
@@ -52,7 +52,7 @@ export type ComputeEventPayload = Schema.Schema.Type<typeof ComputeEventPayload>
|
|
|
52
52
|
|
|
53
53
|
export const ComputeEvent = Schema.Struct({
|
|
54
54
|
payload: ComputeEventPayload,
|
|
55
|
-
}).pipe(Type.
|
|
55
|
+
}).pipe(Type.object({ typename: 'org.dxos.type.compute-event', version: '0.1.0' }));
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Logs event for the compute workflows.
|
|
@@ -75,7 +75,7 @@ export class ComputeEventLogger extends Context.Tag('@dxos/functions/ComputeEven
|
|
|
75
75
|
const tracing = yield* TracingService;
|
|
76
76
|
return {
|
|
77
77
|
log: (event: ComputeEventPayload) => {
|
|
78
|
-
tracing.write(Obj.make(ComputeEvent, { payload: event }));
|
|
78
|
+
tracing.write(Obj.make(ComputeEvent, { payload: event }), tracing.getTraceContext());
|
|
79
79
|
},
|
|
80
80
|
nodeId: undefined,
|
|
81
81
|
};
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
import * as Context from 'effect/Context';
|
|
5
5
|
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Layer from 'effect/Layer';
|
|
6
7
|
|
|
8
|
+
import type { FunctionNotFoundError } from '../errors';
|
|
7
9
|
import { type FunctionDefinition, type InvocationServices } from '../sdk';
|
|
8
10
|
|
|
9
11
|
export class FunctionInvocationService extends Context.Tag('@dxos/functions/FunctionInvocationService')<
|
|
@@ -13,11 +15,23 @@ export class FunctionInvocationService extends Context.Tag('@dxos/functions/Func
|
|
|
13
15
|
functionDef: FunctionDefinition<I, O, any>,
|
|
14
16
|
input: I,
|
|
15
17
|
): Effect.Effect<O, never, InvocationServices>;
|
|
18
|
+
|
|
19
|
+
resolveFunction(key: string): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError>;
|
|
16
20
|
}
|
|
17
21
|
>() {
|
|
22
|
+
static layerNotAvailable = Layer.succeed(FunctionInvocationService, {
|
|
23
|
+
invokeFunction: () => Effect.die('FunctionInvocationService is not avaialble.'),
|
|
24
|
+
resolveFunction: () => Effect.die('FunctionInvocationService is not available.'),
|
|
25
|
+
});
|
|
26
|
+
|
|
18
27
|
static invokeFunction = <I, O>(
|
|
19
28
|
functionDef: FunctionDefinition<I, O, any>,
|
|
20
29
|
input: I,
|
|
21
30
|
): Effect.Effect<O, never, FunctionInvocationService | InvocationServices> =>
|
|
22
31
|
Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.invokeFunction)(functionDef, input);
|
|
32
|
+
|
|
33
|
+
static resolveFunction = (
|
|
34
|
+
key: string,
|
|
35
|
+
): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError, FunctionInvocationService> =>
|
|
36
|
+
Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.resolveFunction)(key);
|
|
23
37
|
}
|
package/src/services/index.ts
CHANGED
package/src/services/tracing.ts
CHANGED
|
@@ -7,10 +7,12 @@ import * as Effect from 'effect/Effect';
|
|
|
7
7
|
import * as Layer from 'effect/Layer';
|
|
8
8
|
|
|
9
9
|
import { AgentStatus } from '@dxos/ai';
|
|
10
|
-
import { Obj } from '@dxos/echo';
|
|
11
|
-
import {
|
|
10
|
+
import { type DXN, Obj } from '@dxos/echo';
|
|
11
|
+
import { ObjectId } from '@dxos/keys';
|
|
12
12
|
import { Message } from '@dxos/types';
|
|
13
13
|
|
|
14
|
+
import type { Trigger } from '../types';
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* Provides a way for compute primitives (functions, workflows, tools)
|
|
16
18
|
* to emit an execution trace as a series of structured ECHO objects.
|
|
@@ -27,15 +29,35 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
27
29
|
* Write an event to the tracing queue.
|
|
28
30
|
* @param event - The event to write. Must be an a typed object.
|
|
29
31
|
*/
|
|
30
|
-
write: (event: Obj.
|
|
32
|
+
write: (event: Obj.Unknown, traceContext: TracingService.TraceContext) => void;
|
|
33
|
+
|
|
34
|
+
traceInvocationStart({
|
|
35
|
+
payload,
|
|
36
|
+
target,
|
|
37
|
+
}: {
|
|
38
|
+
payload: TracingService.FunctionInvocationPayload;
|
|
39
|
+
target?: DXN;
|
|
40
|
+
}): Effect.Effect<TracingService.InvocationTraceData>;
|
|
41
|
+
|
|
42
|
+
traceInvocationEnd({
|
|
43
|
+
trace,
|
|
44
|
+
exception,
|
|
45
|
+
}: {
|
|
46
|
+
trace: TracingService.InvocationTraceData;
|
|
47
|
+
exception?: any;
|
|
48
|
+
}): Effect.Effect<void>;
|
|
31
49
|
}
|
|
32
50
|
>() {
|
|
33
51
|
static noop: Context.Tag.Service<TracingService> = {
|
|
34
52
|
getTraceContext: () => ({}),
|
|
35
53
|
write: () => {},
|
|
54
|
+
traceInvocationStart: () =>
|
|
55
|
+
Effect.sync(() => ({ invocationId: ObjectId.random(), invocationTraceQueue: undefined })),
|
|
56
|
+
traceInvocationEnd: () => Effect.sync(() => {}),
|
|
36
57
|
};
|
|
37
58
|
|
|
38
59
|
static layerNoop: Layer.Layer<TracingService> = Layer.succeed(TracingService, TracingService.noop);
|
|
60
|
+
|
|
39
61
|
/**
|
|
40
62
|
* Creates a TracingService layer that emits events to the parent tracing service.
|
|
41
63
|
*/
|
|
@@ -46,12 +68,25 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
46
68
|
const tracing = yield* TracingService;
|
|
47
69
|
const context = mapContext(tracing.getTraceContext());
|
|
48
70
|
return {
|
|
49
|
-
write: (event) => tracing.write(event),
|
|
71
|
+
write: (event, context) => tracing.write(event, context),
|
|
50
72
|
getTraceContext: () => context,
|
|
73
|
+
traceInvocationStart: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
|
|
74
|
+
traceInvocationEnd: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
|
|
51
75
|
};
|
|
52
76
|
}),
|
|
53
77
|
);
|
|
54
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Create sublayer to trace an invocation.
|
|
81
|
+
* @param data
|
|
82
|
+
* @returns
|
|
83
|
+
*/
|
|
84
|
+
static layerInvocation = (data: TracingService.InvocationTraceData) =>
|
|
85
|
+
TracingService.layerSubframe((context) => ({
|
|
86
|
+
...context,
|
|
87
|
+
currentInvocation: data,
|
|
88
|
+
}));
|
|
89
|
+
|
|
55
90
|
/**
|
|
56
91
|
* Emit the current human-readable execution status.
|
|
57
92
|
*/
|
|
@@ -66,6 +101,7 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
66
101
|
created: new Date().toISOString(),
|
|
67
102
|
...data,
|
|
68
103
|
}),
|
|
104
|
+
tracing.getTraceContext(),
|
|
69
105
|
);
|
|
70
106
|
});
|
|
71
107
|
|
|
@@ -82,12 +118,15 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
|
|
|
82
118
|
...data.properties,
|
|
83
119
|
},
|
|
84
120
|
}),
|
|
121
|
+
tracing.getTraceContext(),
|
|
85
122
|
);
|
|
86
123
|
});
|
|
87
124
|
}
|
|
88
125
|
|
|
89
126
|
export namespace TracingService {
|
|
90
127
|
export interface TraceContext {
|
|
128
|
+
currentInvocation?: InvocationTraceData;
|
|
129
|
+
|
|
91
130
|
/**
|
|
92
131
|
* If this thread sprung from a tool call, this is the ID of the message containing the tool call.
|
|
93
132
|
*/
|
|
@@ -100,6 +139,26 @@ export namespace TracingService {
|
|
|
100
139
|
|
|
101
140
|
debugInfo?: unknown;
|
|
102
141
|
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Trace data for a function/trigger invocation.
|
|
145
|
+
*/
|
|
146
|
+
export interface InvocationTraceData {
|
|
147
|
+
invocationId: ObjectId;
|
|
148
|
+
invocationTraceQueue?: DXN.String;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Payload for a function/trigger invocation.
|
|
153
|
+
*/
|
|
154
|
+
export interface FunctionInvocationPayload {
|
|
155
|
+
data?: any;
|
|
156
|
+
inputNodeId?: string;
|
|
157
|
+
trigger?: {
|
|
158
|
+
id: string;
|
|
159
|
+
kind: Trigger.Kind;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
103
162
|
}
|
|
104
163
|
|
|
105
164
|
/**
|
package/src/types/Function.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
-
import { Annotation, JsonSchema, Obj, Type } from '@dxos/echo';
|
|
7
|
+
import { Annotation, JsonSchema, Obj, Ref, Type } from '@dxos/echo';
|
|
8
8
|
import { SystemTypeAnnotation } from '@dxos/echo/internal';
|
|
9
9
|
|
|
10
10
|
import { Script } from './Script';
|
|
@@ -36,7 +36,7 @@ export const Function = Schema.Struct({
|
|
|
36
36
|
|
|
37
37
|
// Reference to a source script if it exists within ECHO.
|
|
38
38
|
// TODO(burdon): Don't ref ScriptType directly (core).
|
|
39
|
-
source: Schema.optional(
|
|
39
|
+
source: Schema.optional(Ref.Ref(Script)),
|
|
40
40
|
|
|
41
41
|
inputSchema: Schema.optional(JsonSchema.JsonSchema),
|
|
42
42
|
outputSchema: Schema.optional(JsonSchema.JsonSchema),
|
|
@@ -50,11 +50,12 @@ export const Function = Schema.Struct({
|
|
|
50
50
|
// Local binding to a function name.
|
|
51
51
|
binding: Schema.optional(Schema.String),
|
|
52
52
|
}).pipe(
|
|
53
|
-
Type.
|
|
54
|
-
typename: 'dxos.
|
|
53
|
+
Type.object({
|
|
54
|
+
typename: 'org.dxos.type.function',
|
|
55
55
|
version: '0.1.0',
|
|
56
56
|
}),
|
|
57
57
|
Annotation.LabelAnnotation.set(['name']),
|
|
58
|
+
Annotation.IconAnnotation.set({ icon: 'ph--function--regular', hue: 'blue' }),
|
|
58
59
|
SystemTypeAnnotation.set(true),
|
|
59
60
|
);
|
|
60
61
|
|
|
@@ -68,13 +69,15 @@ export const make = (props: Obj.MakeProps<typeof Function>) => Obj.make(Function
|
|
|
68
69
|
* @param source - Source object to copy properties from.
|
|
69
70
|
*/
|
|
70
71
|
export const setFrom = (target: Function, source: Function) => {
|
|
71
|
-
target
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
Obj.change(target, (t) => {
|
|
73
|
+
t.key = source.key ?? target.key;
|
|
74
|
+
t.name = source.name ?? target.name;
|
|
75
|
+
t.version = source.version;
|
|
76
|
+
t.description = source.description;
|
|
77
|
+
t.updated = source.updated;
|
|
78
|
+
// TODO(dmaretskyi): A workaround for an ECHO bug.
|
|
79
|
+
t.inputSchema = source.inputSchema ? JSON.parse(JSON.stringify(source.inputSchema)) : undefined;
|
|
80
|
+
t.outputSchema = source.outputSchema ? JSON.parse(JSON.stringify(source.outputSchema)) : undefined;
|
|
81
|
+
Obj.getMeta(t).keys = JSON.parse(JSON.stringify(Obj.getMeta(source).keys));
|
|
82
|
+
});
|
|
80
83
|
};
|
package/src/types/Script.ts
CHANGED
|
@@ -17,17 +17,22 @@ export const Script = Schema.Struct({
|
|
|
17
17
|
// TODO(burdon): Change to hash of deployed content.
|
|
18
18
|
// Whether source has changed since last deploy.
|
|
19
19
|
changed: Schema.Boolean.pipe(FormInputAnnotation.set(false), Schema.optional),
|
|
20
|
-
source:
|
|
20
|
+
source: Ref.Ref(Text.Text).pipe(FormInputAnnotation.set(false)),
|
|
21
21
|
}).pipe(
|
|
22
|
-
Type.
|
|
23
|
-
typename: 'dxos.
|
|
22
|
+
Type.object({
|
|
23
|
+
typename: 'org.dxos.type.script',
|
|
24
24
|
version: '0.1.0',
|
|
25
25
|
}),
|
|
26
26
|
Annotation.LabelAnnotation.set(['name']),
|
|
27
|
+
Annotation.IconAnnotation.set({
|
|
28
|
+
icon: 'ph--code--regular',
|
|
29
|
+
hue: 'sky',
|
|
30
|
+
}),
|
|
27
31
|
);
|
|
32
|
+
|
|
28
33
|
export interface Script extends Schema.Schema.Type<typeof Script> {}
|
|
29
34
|
|
|
30
35
|
type Props = Omit<Obj.MakeProps<typeof Script>, 'source'> & { source?: string };
|
|
31
36
|
|
|
32
|
-
export const make = ({ source = '', ...props }: Props = {}) =>
|
|
37
|
+
export const make = ({ source = '', ...props }: Props = {}): Script =>
|
|
33
38
|
Obj.make(Script, { ...props, source: Ref.make(Text.make(source)) });
|
package/src/types/Trigger.ts
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
6
|
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
7
|
|
|
8
|
-
import { Obj, QueryAST, Type } from '@dxos/echo';
|
|
8
|
+
import { Annotation, Obj, QueryAST, Ref, Type } from '@dxos/echo';
|
|
9
9
|
import { OptionsAnnotationId, SystemTypeAnnotation } from '@dxos/echo/internal';
|
|
10
10
|
import { DXN } from '@dxos/keys';
|
|
11
|
+
import { Expando } from '@dxos/schema';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Type discriminator for TriggerType.
|
|
@@ -21,15 +22,16 @@ const kindLiteralAnnotations = { title: 'Kind' };
|
|
|
21
22
|
|
|
22
23
|
export const EmailSpec = Schema.Struct({
|
|
23
24
|
kind: Schema.Literal('email').annotations(kindLiteralAnnotations),
|
|
24
|
-
})
|
|
25
|
+
});
|
|
25
26
|
export type EmailSpec = Schema.Schema.Type<typeof EmailSpec>;
|
|
26
27
|
|
|
28
|
+
// TODO(wittjosiah): Remove. Migrate to Subscription triggers once EDGE supports them for feed queries.
|
|
27
29
|
export const QueueSpec = Schema.Struct({
|
|
28
30
|
kind: Schema.Literal('queue').annotations(kindLiteralAnnotations),
|
|
29
31
|
|
|
30
32
|
// TODO(dmaretskyi): Change to a reference.
|
|
31
33
|
queue: DXN.Schema,
|
|
32
|
-
})
|
|
34
|
+
});
|
|
33
35
|
export type QueueSpec = Schema.Schema.Type<typeof QueueSpec>;
|
|
34
36
|
|
|
35
37
|
/**
|
|
@@ -40,7 +42,7 @@ export const SubscriptionSpec = Schema.Struct({
|
|
|
40
42
|
query: Schema.Struct({
|
|
41
43
|
raw: Schema.optional(Schema.String.annotations({ title: 'Query' })),
|
|
42
44
|
ast: QueryAST.Query,
|
|
43
|
-
})
|
|
45
|
+
}),
|
|
44
46
|
options: Schema.optional(
|
|
45
47
|
Schema.Struct({
|
|
46
48
|
// Watch changes to object (not just creation).
|
|
@@ -49,7 +51,7 @@ export const SubscriptionSpec = Schema.Struct({
|
|
|
49
51
|
delay: Schema.optional(Schema.Number.annotations({ title: 'Delay' })),
|
|
50
52
|
}).annotations({ title: 'Options' }),
|
|
51
53
|
),
|
|
52
|
-
})
|
|
54
|
+
});
|
|
53
55
|
export type SubscriptionSpec = Schema.Schema.Type<typeof SubscriptionSpec>;
|
|
54
56
|
|
|
55
57
|
/**
|
|
@@ -61,7 +63,7 @@ export const TimerSpec = Schema.Struct({
|
|
|
61
63
|
title: 'Cron',
|
|
62
64
|
[SchemaAST.ExamplesAnnotationId]: ['0 0 * * *'],
|
|
63
65
|
}),
|
|
64
|
-
})
|
|
66
|
+
});
|
|
65
67
|
export type TimerSpec = Schema.Schema.Type<typeof TimerSpec>;
|
|
66
68
|
|
|
67
69
|
/**
|
|
@@ -80,7 +82,7 @@ export const WebhookSpec = Schema.Struct({
|
|
|
80
82
|
title: 'Port',
|
|
81
83
|
}),
|
|
82
84
|
),
|
|
83
|
-
})
|
|
85
|
+
});
|
|
84
86
|
export type WebhookSpec = Schema.Schema.Type<typeof WebhookSpec>;
|
|
85
87
|
|
|
86
88
|
/**
|
|
@@ -96,12 +98,12 @@ export type Spec = Schema.Schema.Type<typeof Spec>;
|
|
|
96
98
|
* Function is invoked with the `payload` passed as input data.
|
|
97
99
|
* The event that triggers the function is available in the function context.
|
|
98
100
|
*/
|
|
99
|
-
const
|
|
101
|
+
const TriggerSchema = Schema.Struct({
|
|
100
102
|
/**
|
|
101
103
|
* Function or workflow to invoke.
|
|
102
104
|
*/
|
|
103
105
|
// TODO(dmaretskyi): Can be a Ref(FunctionType) or Ref(ComputeGraphType).
|
|
104
|
-
function: Schema.optional(
|
|
106
|
+
function: Schema.optional(Ref.Ref(Expando.Expando).annotations({ title: 'Function' })),
|
|
105
107
|
|
|
106
108
|
/**
|
|
107
109
|
* Only used for workflowSchema.
|
|
@@ -110,31 +112,43 @@ const Trigger_ = Schema.Struct({
|
|
|
110
112
|
*/
|
|
111
113
|
inputNodeId: Schema.optional(Schema.String.annotations({ title: 'Input Node ID' })),
|
|
112
114
|
|
|
115
|
+
// TODO(burdon): NO BOOLEAN PROPERTIES (enabld/disabled/paused, etc.)
|
|
116
|
+
// Need lint rule; or agent rule to require PR review for "boolean" key word.
|
|
113
117
|
enabled: Schema.optional(Schema.Boolean.annotations({ title: 'Enabled' })),
|
|
114
118
|
|
|
115
119
|
spec: Schema.optional(Spec),
|
|
116
120
|
|
|
121
|
+
concurrency: Schema.optional(
|
|
122
|
+
Schema.Number.annotations({
|
|
123
|
+
title: 'Concurrency',
|
|
124
|
+
default: 1,
|
|
125
|
+
description:
|
|
126
|
+
'Maximum number of concurrent invocations of the trigger. For queue triggers, this will process queue items in parallel.',
|
|
127
|
+
}),
|
|
128
|
+
),
|
|
129
|
+
|
|
117
130
|
/**
|
|
118
131
|
* Passed as the input data to the function.
|
|
119
132
|
* Must match the function's input schema.
|
|
120
133
|
*
|
|
121
134
|
* @example
|
|
122
135
|
* {
|
|
123
|
-
* item: '{{
|
|
136
|
+
* item: '{{event.item}}',
|
|
124
137
|
* instructions: 'Summarize and perform entity-extraction'
|
|
125
138
|
* mailbox: { '/': 'dxn:echo:AAA:ZZZ' }
|
|
126
139
|
* }
|
|
127
140
|
*/
|
|
128
|
-
input: Schema.optional(Schema.
|
|
141
|
+
input: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
129
142
|
}).pipe(
|
|
130
|
-
Type.
|
|
131
|
-
typename: 'dxos.
|
|
143
|
+
Type.object({
|
|
144
|
+
typename: 'org.dxos.type.trigger',
|
|
132
145
|
version: '0.1.0',
|
|
133
146
|
}),
|
|
147
|
+
Annotation.IconAnnotation.set({ icon: 'ph--lightning--regular', hue: 'yellow' }),
|
|
134
148
|
SystemTypeAnnotation.set(true),
|
|
135
149
|
);
|
|
136
|
-
|
|
137
|
-
export interface
|
|
138
|
-
export const Trigger:
|
|
150
|
+
|
|
151
|
+
export interface Trigger extends Schema.Schema.Type<typeof TriggerSchema> {}
|
|
152
|
+
export const Trigger: Type.Obj<Trigger> = TriggerSchema as any;
|
|
139
153
|
|
|
140
154
|
export const make = (props: Obj.MakeProps<typeof Trigger>) => Obj.make(Trigger, props);
|