ai-functions 2.1.3 → 2.4.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +90 -1
- package/README.md +38 -0
- package/dist/ai-promise.d.ts +3 -3
- package/dist/ai-promise.d.ts.map +1 -1
- package/dist/ai-promise.js +135 -64
- package/dist/ai-promise.js.map +1 -1
- package/dist/ai-schemas.d.ts +56 -0
- package/dist/ai-schemas.d.ts.map +1 -0
- package/dist/ai-schemas.js +53 -0
- package/dist/ai-schemas.js.map +1 -0
- package/dist/ai.d.ts +16 -242
- package/dist/ai.d.ts.map +1 -1
- package/dist/ai.js +51 -858
- package/dist/ai.js.map +1 -1
- package/dist/batch/anthropic.d.ts +6 -4
- package/dist/batch/anthropic.d.ts.map +1 -1
- package/dist/batch/anthropic.js +83 -145
- package/dist/batch/anthropic.js.map +1 -1
- package/dist/batch/bedrock.d.ts +8 -30
- package/dist/batch/bedrock.d.ts.map +1 -1
- package/dist/batch/bedrock.js +155 -338
- package/dist/batch/bedrock.js.map +1 -1
- package/dist/batch/cloudflare.d.ts +8 -20
- package/dist/batch/cloudflare.d.ts.map +1 -1
- package/dist/batch/cloudflare.js +68 -189
- package/dist/batch/cloudflare.js.map +1 -1
- package/dist/batch/google.d.ts +6 -20
- package/dist/batch/google.d.ts.map +1 -1
- package/dist/batch/google.js +70 -238
- package/dist/batch/google.js.map +1 -1
- package/dist/batch/index.d.ts +4 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +4 -1
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/memory.d.ts +1 -1
- package/dist/batch/memory.d.ts.map +1 -1
- package/dist/batch/memory.js +14 -10
- package/dist/batch/memory.js.map +1 -1
- package/dist/batch/openai.d.ts +11 -14
- package/dist/batch/openai.d.ts.map +1 -1
- package/dist/batch/openai.js +52 -156
- package/dist/batch/openai.js.map +1 -1
- package/dist/batch/provider.d.ts +111 -0
- package/dist/batch/provider.d.ts.map +1 -0
- package/dist/batch/provider.js +233 -0
- package/dist/batch/provider.js.map +1 -0
- package/dist/batch-map.d.ts.map +1 -1
- package/dist/batch-map.js +23 -17
- package/dist/batch-map.js.map +1 -1
- package/dist/batch-queue.d.ts +65 -0
- package/dist/batch-queue.d.ts.map +1 -1
- package/dist/batch-queue.js +169 -14
- package/dist/batch-queue.js.map +1 -1
- package/dist/budget.d.ts.map +1 -1
- package/dist/budget.js +27 -14
- package/dist/budget.js.map +1 -1
- package/dist/cache.d.ts +23 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +36 -15
- package/dist/cache.js.map +1 -1
- package/dist/context.d.ts +26 -8
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +64 -62
- package/dist/context.js.map +1 -1
- package/dist/digital-objects-registry.d.ts +229 -0
- package/dist/digital-objects-registry.d.ts.map +1 -0
- package/dist/digital-objects-registry.js +617 -0
- package/dist/digital-objects-registry.js.map +1 -0
- package/dist/embeddings.d.ts +2 -2
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/eval/runner.d.ts +8 -0
- package/dist/eval/runner.d.ts.map +1 -1
- package/dist/eval/runner.js +41 -35
- package/dist/eval/runner.js.map +1 -1
- package/dist/eval-log/in-memory.d.ts +34 -0
- package/dist/eval-log/in-memory.d.ts.map +1 -0
- package/dist/eval-log/in-memory.js +84 -0
- package/dist/eval-log/in-memory.js.map +1 -0
- package/dist/eval-log/index.d.ts +29 -0
- package/dist/eval-log/index.d.ts.map +1 -0
- package/dist/eval-log/index.js +39 -0
- package/dist/eval-log/index.js.map +1 -0
- package/dist/eval-log/types.d.ts +101 -0
- package/dist/eval-log/types.d.ts.map +1 -0
- package/dist/eval-log/types.js +16 -0
- package/dist/eval-log/types.js.map +1 -0
- package/dist/function-registry.d.ts +176 -0
- package/dist/function-registry.d.ts.map +1 -0
- package/dist/function-registry.js +685 -0
- package/dist/function-registry.js.map +1 -0
- package/dist/generate.d.ts +9 -3
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +18 -18
- package/dist/generate.js.map +1 -1
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -18
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +118 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +187 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/budget.d.ts +84 -0
- package/dist/middleware/budget.d.ts.map +1 -0
- package/dist/middleware/budget.js +110 -0
- package/dist/middleware/budget.js.map +1 -0
- package/dist/middleware/cache.d.ts +103 -0
- package/dist/middleware/cache.d.ts.map +1 -0
- package/dist/middleware/cache.js +228 -0
- package/dist/middleware/cache.js.map +1 -0
- package/dist/middleware/embed-cache.d.ts +99 -0
- package/dist/middleware/embed-cache.d.ts.map +1 -0
- package/dist/middleware/embed-cache.js +128 -0
- package/dist/middleware/embed-cache.js.map +1 -0
- package/dist/middleware/index.d.ts +11 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/trace.d.ts +103 -0
- package/dist/middleware/trace.d.ts.map +1 -0
- package/dist/middleware/trace.js +176 -0
- package/dist/middleware/trace.js.map +1 -0
- package/dist/primitives.d.ts +120 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +398 -26
- package/dist/primitives.js.map +1 -1
- package/dist/retry.d.ts +66 -1
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +115 -8
- package/dist/retry.js.map +1 -1
- package/dist/sandbox.d.ts +36 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +44 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/schema.js +2 -2
- package/dist/schema.js.map +1 -1
- package/dist/telemetry.d.ts +128 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +285 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +6 -1
- package/dist/template.js.map +1 -1
- package/dist/tool-orchestration.d.ts +66 -4
- package/dist/tool-orchestration.d.ts.map +1 -1
- package/dist/tool-orchestration.js +123 -23
- package/dist/tool-orchestration.js.map +1 -1
- package/dist/type-guards.d.ts +28 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +29 -0
- package/dist/type-guards.js.map +1 -0
- package/dist/types.d.ts +155 -19
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +36 -1
- package/dist/types.js.map +1 -1
- package/dist/wrap-for-v3.d.ts +80 -0
- package/dist/wrap-for-v3.d.ts.map +1 -0
- package/dist/wrap-for-v3.js +89 -0
- package/dist/wrap-for-v3.js.map +1 -0
- package/examples/00-quickstart.ts +232 -0
- package/examples/01-rag-chatbot.ts +212 -0
- package/examples/02-multi-agent-research.ts +290 -0
- package/examples/03-email-classification.ts +379 -0
- package/examples/04-content-moderation.ts +400 -0
- package/examples/05-document-extraction.ts +455 -0
- package/examples/06-streaming-chat-nextjs.ts +437 -0
- package/examples/07-cloudflare-worker.ts +483 -0
- package/examples/08-batch-processing.ts +491 -0
- package/examples/09-budget-constrained.ts +527 -0
- package/examples/10-tool-orchestration.ts +565 -0
- package/examples/11-retry-resilience.ts +403 -0
- package/examples/12-caching-strategies.ts +422 -0
- package/examples/README.md +145 -0
- package/package.json +29 -25
- package/src/ai-promise.ts +226 -140
- package/src/ai-schemas.ts +122 -0
- package/src/ai.ts +71 -1176
- package/src/batch/anthropic.ts +96 -161
- package/src/batch/bedrock.ts +203 -454
- package/src/batch/cloudflare.ts +99 -282
- package/src/batch/google.ts +91 -297
- package/src/batch/index.ts +4 -1
- package/src/batch/memory.ts +15 -10
- package/src/batch/openai.ts +65 -193
- package/src/batch/provider.ts +336 -0
- package/src/batch-map.ts +29 -24
- package/src/batch-queue.ts +200 -11
- package/src/budget.ts +31 -18
- package/src/cache.ts +45 -17
- package/src/context.ts +106 -77
- package/src/digital-objects-registry.ts +750 -0
- package/src/errors.ts +37 -0
- package/src/eval/runner.ts +60 -36
- package/src/eval-log/in-memory.ts +90 -0
- package/src/eval-log/index.ts +46 -0
- package/src/eval-log/types.ts +110 -0
- package/src/function-registry.ts +874 -0
- package/src/generate.ts +33 -28
- package/src/index.ts +122 -21
- package/src/logger.ts +232 -0
- package/src/middleware/budget.ts +171 -0
- package/src/middleware/cache.ts +299 -0
- package/src/middleware/embed-cache.ts +195 -0
- package/src/middleware/index.ts +23 -0
- package/src/middleware/trace.ts +248 -0
- package/src/primitives.ts +589 -62
- package/src/retry.ts +144 -18
- package/src/sandbox.ts +52 -0
- package/src/schema.ts +8 -8
- package/src/telemetry.ts +403 -0
- package/src/template.ts +8 -4
- package/src/tool-orchestration.ts +213 -48
- package/src/type-guards.ts +31 -0
- package/src/types.ts +186 -27
- package/src/wrap-for-v3.ts +105 -0
- package/test/ai-promise.test.ts +1080 -0
- package/test/ai-proxy.test.ts +1 -1
- package/test/batch-autosubmit-errors.test.ts +49 -37
- package/test/batch-blog-posts.test.ts +87 -129
- package/test/core-functions.test.ts +183 -579
- package/test/decide.test.ts +154 -322
- package/test/define.test.ts +211 -8
- package/test/digital-objects-registry.test.ts +760 -0
- package/test/embedding-cache-middleware.test.ts +140 -0
- package/test/fill-template.test.ts +89 -0
- package/test/generate-core.test.ts +140 -229
- package/test/implicit-batch.test.ts +22 -65
- package/test/retry-policy-integration.test.ts +117 -0
- package/test/sandbox-execution.test.ts +155 -0
- package/test/schema.test.ts +55 -19
- package/test/template.test.ts +1164 -0
- package/test/tool-orchestration.test.ts +270 -0
- package/test/wrap-for-v3.test.ts +612 -0
- package/vitest.config.js +6 -0
- package/vitest.config.ts +20 -0
- package/LICENSE +0 -21
- package/dist/rpc/auth.d.ts +0 -69
- package/dist/rpc/auth.d.ts.map +0 -1
- package/dist/rpc/auth.js +0 -136
- package/dist/rpc/auth.js.map +0 -1
- package/dist/rpc/client.d.ts +0 -62
- package/dist/rpc/client.d.ts.map +0 -1
- package/dist/rpc/client.js +0 -103
- package/dist/rpc/client.js.map +0 -1
- package/dist/rpc/deferred.d.ts +0 -60
- package/dist/rpc/deferred.d.ts.map +0 -1
- package/dist/rpc/deferred.js +0 -96
- package/dist/rpc/deferred.js.map +0 -1
- package/dist/rpc/index.d.ts +0 -22
- package/dist/rpc/index.d.ts.map +0 -1
- package/dist/rpc/index.js +0 -38
- package/dist/rpc/index.js.map +0 -1
- package/dist/rpc/local.d.ts +0 -42
- package/dist/rpc/local.d.ts.map +0 -1
- package/dist/rpc/local.js +0 -50
- package/dist/rpc/local.js.map +0 -1
- package/dist/rpc/server.d.ts +0 -165
- package/dist/rpc/server.d.ts.map +0 -1
- package/dist/rpc/server.js +0 -405
- package/dist/rpc/server.js.map +0 -1
- package/dist/rpc/session.d.ts +0 -32
- package/dist/rpc/session.d.ts.map +0 -1
- package/dist/rpc/session.js +0 -43
- package/dist/rpc/session.js.map +0 -1
- package/dist/rpc/transport.d.ts +0 -306
- package/dist/rpc/transport.d.ts.map +0 -1
- package/dist/rpc/transport.js +0 -731
- package/dist/rpc/transport.js.map +0 -1
- package/src/batch/anthropic.js +0 -256
- package/src/batch/bedrock.js +0 -584
- package/src/batch/cloudflare.js +0 -287
- package/src/batch/google.js +0 -359
- package/src/batch/index.js +0 -30
- package/src/batch/memory.js +0 -187
- package/src/batch/openai.js +0 -402
- package/src/eval/index.js +0 -7
- package/src/eval/models.js +0 -119
- package/src/eval/runner.js +0 -147
- package/test/schema.test.js +0 -96
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for DigitalObjectsFunctionRegistry
|
|
3
|
+
*
|
|
4
|
+
* This tests the persistent function registry implementation using digital-objects.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, beforeEach } from 'vitest'
|
|
8
|
+
import { createMemoryProvider } from 'digital-objects'
|
|
9
|
+
import type { DigitalObjectsProvider } from 'digital-objects'
|
|
10
|
+
import {
|
|
11
|
+
createDigitalObjectsRegistry,
|
|
12
|
+
DigitalObjectsFunctionRegistry,
|
|
13
|
+
FUNCTION_NOUNS,
|
|
14
|
+
FUNCTION_VERBS,
|
|
15
|
+
} from '../src/digital-objects-registry.js'
|
|
16
|
+
import { defineFunction } from '../src/index.js'
|
|
17
|
+
import type { DefinedFunction } from '../src/types.js'
|
|
18
|
+
|
|
19
|
+
describe('createDigitalObjectsRegistry', () => {
|
|
20
|
+
let provider: DigitalObjectsProvider
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
provider = createMemoryProvider()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('creates a registry with nouns defined', async () => {
|
|
27
|
+
const registry = await createDigitalObjectsRegistry({ provider })
|
|
28
|
+
|
|
29
|
+
// Check that nouns were defined
|
|
30
|
+
const nouns = await provider.listNouns()
|
|
31
|
+
const nounNames = nouns.map((n) => n.name)
|
|
32
|
+
|
|
33
|
+
expect(nounNames).toContain(FUNCTION_NOUNS.CODE)
|
|
34
|
+
expect(nounNames).toContain(FUNCTION_NOUNS.GENERATIVE)
|
|
35
|
+
expect(nounNames).toContain(FUNCTION_NOUNS.AGENTIC)
|
|
36
|
+
expect(nounNames).toContain(FUNCTION_NOUNS.HUMAN)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('creates a registry with verbs defined', async () => {
|
|
40
|
+
const registry = await createDigitalObjectsRegistry({ provider })
|
|
41
|
+
|
|
42
|
+
// Check that verbs were defined
|
|
43
|
+
const verbs = await provider.listVerbs()
|
|
44
|
+
const verbNames = verbs.map((v) => v.name)
|
|
45
|
+
|
|
46
|
+
expect(verbNames).toContain(FUNCTION_VERBS.DEFINE)
|
|
47
|
+
expect(verbNames).toContain(FUNCTION_VERBS.CALL)
|
|
48
|
+
expect(verbNames).toContain(FUNCTION_VERBS.COMPLETE)
|
|
49
|
+
expect(verbNames).toContain(FUNCTION_VERBS.FAIL)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('can skip auto-initialization', async () => {
|
|
53
|
+
const registry = await createDigitalObjectsRegistry({
|
|
54
|
+
provider,
|
|
55
|
+
autoInitialize: false,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Should not have defined nouns yet
|
|
59
|
+
const nouns = await provider.listNouns()
|
|
60
|
+
expect(nouns.length).toBe(0)
|
|
61
|
+
|
|
62
|
+
// Can manually initialize
|
|
63
|
+
await registry.initialize()
|
|
64
|
+
const nounsAfter = await provider.listNouns()
|
|
65
|
+
expect(nounsAfter.length).toBe(4)
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('DigitalObjectsFunctionRegistry', () => {
|
|
70
|
+
let provider: DigitalObjectsProvider
|
|
71
|
+
let registry: DigitalObjectsFunctionRegistry
|
|
72
|
+
|
|
73
|
+
beforeEach(async () => {
|
|
74
|
+
provider = createMemoryProvider()
|
|
75
|
+
registry = await createDigitalObjectsRegistry({ provider })
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('set()', () => {
|
|
79
|
+
it('stores function definitions as Things', async () => {
|
|
80
|
+
const fn = defineFunction({
|
|
81
|
+
type: 'generative',
|
|
82
|
+
name: 'summarize',
|
|
83
|
+
args: { text: 'Text to summarize' },
|
|
84
|
+
output: 'string',
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
registry.set('summarize', fn)
|
|
88
|
+
|
|
89
|
+
// Wait for async storage to complete
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
91
|
+
|
|
92
|
+
// Check that it was stored as a Thing
|
|
93
|
+
const things = await provider.find(FUNCTION_NOUNS.GENERATIVE, { name: 'summarize' })
|
|
94
|
+
expect(things.length).toBe(1)
|
|
95
|
+
expect(things[0].data).toMatchObject({
|
|
96
|
+
name: 'summarize',
|
|
97
|
+
type: 'generative',
|
|
98
|
+
output: 'string',
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('stores different function types in their respective nouns', async () => {
|
|
103
|
+
const codeFn = defineFunction({
|
|
104
|
+
type: 'code',
|
|
105
|
+
name: 'implement',
|
|
106
|
+
args: { spec: 'Spec' },
|
|
107
|
+
language: 'typescript',
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const agenticFn = defineFunction({
|
|
111
|
+
type: 'agentic',
|
|
112
|
+
name: 'research',
|
|
113
|
+
args: { topic: 'Topic' },
|
|
114
|
+
instructions: 'Research thoroughly',
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const humanFn = defineFunction({
|
|
118
|
+
type: 'human',
|
|
119
|
+
name: 'approve',
|
|
120
|
+
args: { amount: 'Amount' },
|
|
121
|
+
channel: 'web',
|
|
122
|
+
instructions: 'Review and approve',
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
registry.set('implement', codeFn)
|
|
126
|
+
registry.set('research', agenticFn)
|
|
127
|
+
registry.set('approve', humanFn)
|
|
128
|
+
|
|
129
|
+
await new Promise((resolve) => setTimeout(resolve, 10))
|
|
130
|
+
|
|
131
|
+
const codeThings = await provider.find(FUNCTION_NOUNS.CODE, { name: 'implement' })
|
|
132
|
+
const agenticThings = await provider.find(FUNCTION_NOUNS.AGENTIC, { name: 'research' })
|
|
133
|
+
const humanThings = await provider.find(FUNCTION_NOUNS.HUMAN, { name: 'approve' })
|
|
134
|
+
|
|
135
|
+
expect(codeThings.length).toBe(1)
|
|
136
|
+
expect(agenticThings.length).toBe(1)
|
|
137
|
+
expect(humanThings.length).toBe(1)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
describe('get()', () => {
|
|
142
|
+
it('retrieves function definitions from cache', () => {
|
|
143
|
+
const fn = defineFunction({
|
|
144
|
+
type: 'generative',
|
|
145
|
+
name: 'translate',
|
|
146
|
+
args: { text: 'Text', lang: 'Target language' },
|
|
147
|
+
output: 'string',
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
registry.set('translate', fn)
|
|
151
|
+
|
|
152
|
+
const retrieved = registry.get('translate')
|
|
153
|
+
expect(retrieved).toBe(fn)
|
|
154
|
+
expect(retrieved?.definition.name).toBe('translate')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('returns undefined for non-existent functions', () => {
|
|
158
|
+
const result = registry.get('nonexistent')
|
|
159
|
+
expect(result).toBeUndefined()
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
describe('getAsync()', () => {
|
|
164
|
+
it('retrieves function definitions from storage', async () => {
|
|
165
|
+
const fn = defineFunction({
|
|
166
|
+
type: 'generative',
|
|
167
|
+
name: 'analyze',
|
|
168
|
+
args: { data: 'Data to analyze' },
|
|
169
|
+
output: 'string',
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
await registry.setAsync('analyze', fn)
|
|
173
|
+
|
|
174
|
+
// Create a new registry to simulate fresh load
|
|
175
|
+
const newRegistry = await createDigitalObjectsRegistry({ provider })
|
|
176
|
+
|
|
177
|
+
const retrieved = await newRegistry.getAsync('analyze')
|
|
178
|
+
expect(retrieved).toBeDefined()
|
|
179
|
+
expect(retrieved?.definition.name).toBe('analyze')
|
|
180
|
+
expect(retrieved?.definition.type).toBe('generative')
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
describe('has()', () => {
|
|
185
|
+
it('checks if function exists in cache', () => {
|
|
186
|
+
expect(registry.has('test')).toBe(false)
|
|
187
|
+
|
|
188
|
+
const fn = defineFunction({
|
|
189
|
+
type: 'generative',
|
|
190
|
+
name: 'test',
|
|
191
|
+
args: {},
|
|
192
|
+
output: 'string',
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
registry.set('test', fn)
|
|
196
|
+
expect(registry.has('test')).toBe(true)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe('hasAsync()', () => {
|
|
201
|
+
it('checks if function exists in storage', async () => {
|
|
202
|
+
const fn = defineFunction({
|
|
203
|
+
type: 'generative',
|
|
204
|
+
name: 'stored',
|
|
205
|
+
args: {},
|
|
206
|
+
output: 'string',
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
await registry.setAsync('stored', fn)
|
|
210
|
+
|
|
211
|
+
// Create new registry
|
|
212
|
+
const newRegistry = await createDigitalObjectsRegistry({ provider })
|
|
213
|
+
|
|
214
|
+
expect(await newRegistry.hasAsync('stored')).toBe(true)
|
|
215
|
+
expect(await newRegistry.hasAsync('notexist')).toBe(false)
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('delete()', () => {
|
|
220
|
+
it('removes functions from cache', () => {
|
|
221
|
+
const fn = defineFunction({
|
|
222
|
+
type: 'generative',
|
|
223
|
+
name: 'toDelete',
|
|
224
|
+
args: {},
|
|
225
|
+
output: 'string',
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
registry.set('toDelete', fn)
|
|
229
|
+
expect(registry.has('toDelete')).toBe(true)
|
|
230
|
+
|
|
231
|
+
const result = registry.delete('toDelete')
|
|
232
|
+
expect(result).toBe(true)
|
|
233
|
+
expect(registry.has('toDelete')).toBe(false)
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('returns false for non-existent functions', () => {
|
|
237
|
+
const result = registry.delete('nonexistent')
|
|
238
|
+
expect(result).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('deleteAsync()', () => {
|
|
243
|
+
it('removes functions from storage', async () => {
|
|
244
|
+
const fn = defineFunction({
|
|
245
|
+
type: 'generative',
|
|
246
|
+
name: 'toRemove',
|
|
247
|
+
args: {},
|
|
248
|
+
output: 'string',
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
await registry.setAsync('toRemove', fn)
|
|
252
|
+
|
|
253
|
+
// Verify it exists
|
|
254
|
+
const things = await provider.find(FUNCTION_NOUNS.GENERATIVE, { name: 'toRemove' })
|
|
255
|
+
expect(things.length).toBe(1)
|
|
256
|
+
|
|
257
|
+
// Delete it
|
|
258
|
+
const result = await registry.deleteAsync('toRemove')
|
|
259
|
+
expect(result).toBe(true)
|
|
260
|
+
|
|
261
|
+
// Verify it's gone
|
|
262
|
+
const thingsAfter = await provider.find(FUNCTION_NOUNS.GENERATIVE, { name: 'toRemove' })
|
|
263
|
+
expect(thingsAfter.length).toBe(0)
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
describe('list() and listAsync()', () => {
|
|
268
|
+
it('list() returns all function names from cache', () => {
|
|
269
|
+
const fn1 = defineFunction({
|
|
270
|
+
type: 'generative',
|
|
271
|
+
name: 'func1',
|
|
272
|
+
args: {},
|
|
273
|
+
output: 'string',
|
|
274
|
+
})
|
|
275
|
+
const fn2 = defineFunction({
|
|
276
|
+
type: 'code',
|
|
277
|
+
name: 'func2',
|
|
278
|
+
args: {},
|
|
279
|
+
language: 'typescript',
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
registry.set('func1', fn1)
|
|
283
|
+
registry.set('func2', fn2)
|
|
284
|
+
|
|
285
|
+
const names = registry.list()
|
|
286
|
+
expect(names).toContain('func1')
|
|
287
|
+
expect(names).toContain('func2')
|
|
288
|
+
expect(names.length).toBe(2)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('listAsync() returns all function names including from storage', async () => {
|
|
292
|
+
const fn1 = defineFunction({
|
|
293
|
+
type: 'generative',
|
|
294
|
+
name: 'funcA',
|
|
295
|
+
args: {},
|
|
296
|
+
output: 'string',
|
|
297
|
+
})
|
|
298
|
+
const fn2 = defineFunction({
|
|
299
|
+
type: 'agentic',
|
|
300
|
+
name: 'funcB',
|
|
301
|
+
args: {},
|
|
302
|
+
instructions: 'Do something',
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
await registry.setAsync('funcA', fn1)
|
|
306
|
+
await registry.setAsync('funcB', fn2)
|
|
307
|
+
|
|
308
|
+
const names = await registry.listAsync()
|
|
309
|
+
expect(names).toContain('funcA')
|
|
310
|
+
expect(names).toContain('funcB')
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
describe('getAll() equivalent - list + get pattern', () => {
|
|
315
|
+
it('returns all functions', () => {
|
|
316
|
+
const fn1 = defineFunction({
|
|
317
|
+
type: 'generative',
|
|
318
|
+
name: 'alpha',
|
|
319
|
+
args: {},
|
|
320
|
+
output: 'string',
|
|
321
|
+
})
|
|
322
|
+
const fn2 = defineFunction({
|
|
323
|
+
type: 'generative',
|
|
324
|
+
name: 'beta',
|
|
325
|
+
args: {},
|
|
326
|
+
output: 'string',
|
|
327
|
+
})
|
|
328
|
+
const fn3 = defineFunction({
|
|
329
|
+
type: 'code',
|
|
330
|
+
name: 'gamma',
|
|
331
|
+
args: {},
|
|
332
|
+
language: 'python',
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
registry.set('alpha', fn1)
|
|
336
|
+
registry.set('beta', fn2)
|
|
337
|
+
registry.set('gamma', fn3)
|
|
338
|
+
|
|
339
|
+
// getAll pattern: list all names then get each
|
|
340
|
+
const allFunctions = registry.list().map((name) => registry.get(name))
|
|
341
|
+
|
|
342
|
+
expect(allFunctions.length).toBe(3)
|
|
343
|
+
expect(allFunctions.map((f) => f?.definition.name)).toContain('alpha')
|
|
344
|
+
expect(allFunctions.map((f) => f?.definition.name)).toContain('beta')
|
|
345
|
+
expect(allFunctions.map((f) => f?.definition.name)).toContain('gamma')
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
describe('clear() and clearAsync()', () => {
|
|
350
|
+
it('clear() removes all functions from cache', () => {
|
|
351
|
+
const fn1 = defineFunction({
|
|
352
|
+
type: 'generative',
|
|
353
|
+
name: 'a',
|
|
354
|
+
args: {},
|
|
355
|
+
output: 'string',
|
|
356
|
+
})
|
|
357
|
+
const fn2 = defineFunction({
|
|
358
|
+
type: 'generative',
|
|
359
|
+
name: 'b',
|
|
360
|
+
args: {},
|
|
361
|
+
output: 'string',
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
registry.set('a', fn1)
|
|
365
|
+
registry.set('b', fn2)
|
|
366
|
+
expect(registry.list().length).toBe(2)
|
|
367
|
+
|
|
368
|
+
registry.clear()
|
|
369
|
+
expect(registry.list().length).toBe(0)
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
it('clearAsync() removes all functions from storage', async () => {
|
|
373
|
+
const fn = defineFunction({
|
|
374
|
+
type: 'generative',
|
|
375
|
+
name: 'toClear',
|
|
376
|
+
args: {},
|
|
377
|
+
output: 'string',
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
await registry.setAsync('toClear', fn)
|
|
381
|
+
|
|
382
|
+
// Verify stored
|
|
383
|
+
let things = await provider.list(FUNCTION_NOUNS.GENERATIVE)
|
|
384
|
+
expect(things.length).toBe(1)
|
|
385
|
+
|
|
386
|
+
await registry.clearAsync()
|
|
387
|
+
|
|
388
|
+
// Verify cleared
|
|
389
|
+
things = await provider.list(FUNCTION_NOUNS.GENERATIVE)
|
|
390
|
+
expect(things.length).toBe(0)
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
describe('DigitalObjectsFunctionRegistry - Action Tracking', () => {
|
|
396
|
+
let provider: DigitalObjectsProvider
|
|
397
|
+
let registry: DigitalObjectsFunctionRegistry
|
|
398
|
+
|
|
399
|
+
beforeEach(async () => {
|
|
400
|
+
provider = createMemoryProvider()
|
|
401
|
+
registry = await createDigitalObjectsRegistry({ provider })
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
describe('trackCall()', () => {
|
|
405
|
+
it('creates an Action for function invocation', async () => {
|
|
406
|
+
// First, store a function
|
|
407
|
+
const fn = defineFunction({
|
|
408
|
+
type: 'generative',
|
|
409
|
+
name: 'greet',
|
|
410
|
+
args: { name: 'Name to greet' },
|
|
411
|
+
output: 'string',
|
|
412
|
+
})
|
|
413
|
+
await registry.setAsync('greet', fn)
|
|
414
|
+
|
|
415
|
+
// Track a call
|
|
416
|
+
const callAction = await registry.trackCall('greet', { name: 'Alice' })
|
|
417
|
+
|
|
418
|
+
expect(callAction).toBeDefined()
|
|
419
|
+
expect(callAction.verb).toBe(FUNCTION_VERBS.CALL)
|
|
420
|
+
expect(callAction.data).toMatchObject({
|
|
421
|
+
args: { name: 'Alice' },
|
|
422
|
+
})
|
|
423
|
+
expect(callAction.id).toBeDefined()
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
it('links call action to the function Thing', async () => {
|
|
427
|
+
const fn = defineFunction({
|
|
428
|
+
type: 'generative',
|
|
429
|
+
name: 'process',
|
|
430
|
+
args: { input: 'Input data' },
|
|
431
|
+
output: 'string',
|
|
432
|
+
})
|
|
433
|
+
const thing = await registry.setAsync('process', fn)
|
|
434
|
+
|
|
435
|
+
const callAction = await registry.trackCall('process', { input: 'test' })
|
|
436
|
+
|
|
437
|
+
// The action's object should be the function thing's ID
|
|
438
|
+
expect(callAction.object).toBe(thing.id)
|
|
439
|
+
})
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
describe('trackCompletion()', () => {
|
|
443
|
+
it('creates an Action for successful completion', async () => {
|
|
444
|
+
const fn = defineFunction({
|
|
445
|
+
type: 'generative',
|
|
446
|
+
name: 'compute',
|
|
447
|
+
args: { value: 'Input value' },
|
|
448
|
+
output: 'string',
|
|
449
|
+
})
|
|
450
|
+
await registry.setAsync('compute', fn)
|
|
451
|
+
|
|
452
|
+
const callAction = await registry.trackCall('compute', { value: 42 })
|
|
453
|
+
|
|
454
|
+
const completionAction = await registry.trackCompletion(callAction.id, 'Result: 84', 150)
|
|
455
|
+
|
|
456
|
+
expect(completionAction).toBeDefined()
|
|
457
|
+
expect(completionAction.verb).toBe(FUNCTION_VERBS.COMPLETE)
|
|
458
|
+
expect(completionAction.object).toBe(callAction.id)
|
|
459
|
+
expect(completionAction.data).toMatchObject({
|
|
460
|
+
result: 'Result: 84',
|
|
461
|
+
duration: 150,
|
|
462
|
+
})
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
it('stores the result in the action data', async () => {
|
|
466
|
+
const fn = defineFunction({
|
|
467
|
+
type: 'generative',
|
|
468
|
+
name: 'calculate',
|
|
469
|
+
args: {},
|
|
470
|
+
output: 'string',
|
|
471
|
+
})
|
|
472
|
+
await registry.setAsync('calculate', fn)
|
|
473
|
+
|
|
474
|
+
const callAction = await registry.trackCall('calculate', {})
|
|
475
|
+
const completionAction = await registry.trackCompletion(callAction.id, {
|
|
476
|
+
computed: true,
|
|
477
|
+
value: 100,
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
expect(completionAction.data?.result).toEqual({ computed: true, value: 100 })
|
|
481
|
+
})
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
describe('trackFailure()', () => {
|
|
485
|
+
it('creates an Action for failed execution', async () => {
|
|
486
|
+
const fn = defineFunction({
|
|
487
|
+
type: 'generative',
|
|
488
|
+
name: 'failing',
|
|
489
|
+
args: {},
|
|
490
|
+
output: 'string',
|
|
491
|
+
})
|
|
492
|
+
await registry.setAsync('failing', fn)
|
|
493
|
+
|
|
494
|
+
const callAction = await registry.trackCall('failing', {})
|
|
495
|
+
|
|
496
|
+
const failureAction = await registry.trackFailure(callAction.id, 'Connection timeout', 5000)
|
|
497
|
+
|
|
498
|
+
expect(failureAction).toBeDefined()
|
|
499
|
+
expect(failureAction.verb).toBe(FUNCTION_VERBS.FAIL)
|
|
500
|
+
expect(failureAction.object).toBe(callAction.id)
|
|
501
|
+
expect(failureAction.data).toMatchObject({
|
|
502
|
+
error: 'Connection timeout',
|
|
503
|
+
duration: 5000,
|
|
504
|
+
})
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
it('stores error message in action data', async () => {
|
|
508
|
+
const fn = defineFunction({
|
|
509
|
+
type: 'generative',
|
|
510
|
+
name: 'erroring',
|
|
511
|
+
args: {},
|
|
512
|
+
output: 'string',
|
|
513
|
+
})
|
|
514
|
+
await registry.setAsync('erroring', fn)
|
|
515
|
+
|
|
516
|
+
const callAction = await registry.trackCall('erroring', {})
|
|
517
|
+
const failureAction = await registry.trackFailure(callAction.id, 'Invalid input provided')
|
|
518
|
+
|
|
519
|
+
expect(failureAction.data?.error).toBe('Invalid input provided')
|
|
520
|
+
})
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
describe('getCallHistory()', () => {
|
|
524
|
+
it('returns calls for a specific function', async () => {
|
|
525
|
+
const fn = defineFunction({
|
|
526
|
+
type: 'generative',
|
|
527
|
+
name: 'tracked',
|
|
528
|
+
args: { x: 'Input' },
|
|
529
|
+
output: 'string',
|
|
530
|
+
})
|
|
531
|
+
await registry.setAsync('tracked', fn)
|
|
532
|
+
|
|
533
|
+
// Make multiple calls
|
|
534
|
+
await registry.trackCall('tracked', { x: 1 })
|
|
535
|
+
await registry.trackCall('tracked', { x: 2 })
|
|
536
|
+
await registry.trackCall('tracked', { x: 3 })
|
|
537
|
+
|
|
538
|
+
const history = await registry.getCallHistory('tracked')
|
|
539
|
+
|
|
540
|
+
expect(history.length).toBe(3)
|
|
541
|
+
expect(history.every((a) => a.verb === FUNCTION_VERBS.CALL)).toBe(true)
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
it('returns empty array for function with no calls', async () => {
|
|
545
|
+
const fn = defineFunction({
|
|
546
|
+
type: 'generative',
|
|
547
|
+
name: 'unused',
|
|
548
|
+
args: {},
|
|
549
|
+
output: 'string',
|
|
550
|
+
})
|
|
551
|
+
await registry.setAsync('unused', fn)
|
|
552
|
+
|
|
553
|
+
const history = await registry.getCallHistory('unused')
|
|
554
|
+
expect(history).toEqual([])
|
|
555
|
+
})
|
|
556
|
+
|
|
557
|
+
it('returns empty array for non-existent function', async () => {
|
|
558
|
+
const history = await registry.getCallHistory('nonexistent')
|
|
559
|
+
expect(history).toEqual([])
|
|
560
|
+
})
|
|
561
|
+
|
|
562
|
+
it('only returns calls for the specified function', async () => {
|
|
563
|
+
const fn1 = defineFunction({
|
|
564
|
+
type: 'generative',
|
|
565
|
+
name: 'funcA',
|
|
566
|
+
args: {},
|
|
567
|
+
output: 'string',
|
|
568
|
+
})
|
|
569
|
+
const fn2 = defineFunction({
|
|
570
|
+
type: 'generative',
|
|
571
|
+
name: 'funcB',
|
|
572
|
+
args: {},
|
|
573
|
+
output: 'string',
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
await registry.setAsync('funcA', fn1)
|
|
577
|
+
await registry.setAsync('funcB', fn2)
|
|
578
|
+
|
|
579
|
+
await registry.trackCall('funcA', { data: 'a1' })
|
|
580
|
+
await registry.trackCall('funcA', { data: 'a2' })
|
|
581
|
+
await registry.trackCall('funcB', { data: 'b1' })
|
|
582
|
+
|
|
583
|
+
const historyA = await registry.getCallHistory('funcA')
|
|
584
|
+
const historyB = await registry.getCallHistory('funcB')
|
|
585
|
+
|
|
586
|
+
expect(historyA.length).toBe(2)
|
|
587
|
+
expect(historyB.length).toBe(1)
|
|
588
|
+
})
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
describe('getRecentCalls()', () => {
|
|
592
|
+
it('returns recent calls across all functions', async () => {
|
|
593
|
+
const fn1 = defineFunction({
|
|
594
|
+
type: 'generative',
|
|
595
|
+
name: 'recent1',
|
|
596
|
+
args: {},
|
|
597
|
+
output: 'string',
|
|
598
|
+
})
|
|
599
|
+
const fn2 = defineFunction({
|
|
600
|
+
type: 'code',
|
|
601
|
+
name: 'recent2',
|
|
602
|
+
args: {},
|
|
603
|
+
language: 'typescript',
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
await registry.setAsync('recent1', fn1)
|
|
607
|
+
await registry.setAsync('recent2', fn2)
|
|
608
|
+
|
|
609
|
+
await registry.trackCall('recent1', { call: 1 })
|
|
610
|
+
await registry.trackCall('recent2', { call: 2 })
|
|
611
|
+
await registry.trackCall('recent1', { call: 3 })
|
|
612
|
+
|
|
613
|
+
const recentCalls = await registry.getRecentCalls()
|
|
614
|
+
|
|
615
|
+
expect(recentCalls.length).toBe(3)
|
|
616
|
+
expect(recentCalls.every((a) => a.verb === FUNCTION_VERBS.CALL)).toBe(true)
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
it('respects the limit parameter', async () => {
|
|
620
|
+
const fn = defineFunction({
|
|
621
|
+
type: 'generative',
|
|
622
|
+
name: 'limited',
|
|
623
|
+
args: {},
|
|
624
|
+
output: 'string',
|
|
625
|
+
})
|
|
626
|
+
await registry.setAsync('limited', fn)
|
|
627
|
+
|
|
628
|
+
// Make 5 calls
|
|
629
|
+
for (let i = 0; i < 5; i++) {
|
|
630
|
+
await registry.trackCall('limited', { index: i })
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const limited = await registry.getRecentCalls(3)
|
|
634
|
+
expect(limited.length).toBe(3)
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
it('defaults to 10 results', async () => {
|
|
638
|
+
const fn = defineFunction({
|
|
639
|
+
type: 'generative',
|
|
640
|
+
name: 'many',
|
|
641
|
+
args: {},
|
|
642
|
+
output: 'string',
|
|
643
|
+
})
|
|
644
|
+
await registry.setAsync('many', fn)
|
|
645
|
+
|
|
646
|
+
// Make 15 calls
|
|
647
|
+
for (let i = 0; i < 15; i++) {
|
|
648
|
+
await registry.trackCall('many', { index: i })
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const recent = await registry.getRecentCalls()
|
|
652
|
+
expect(recent.length).toBe(10)
|
|
653
|
+
})
|
|
654
|
+
})
|
|
655
|
+
|
|
656
|
+
describe('getProvider()', () => {
|
|
657
|
+
it('returns the underlying provider', () => {
|
|
658
|
+
const returnedProvider = registry.getProvider()
|
|
659
|
+
expect(returnedProvider).toBe(provider)
|
|
660
|
+
})
|
|
661
|
+
})
|
|
662
|
+
})
|
|
663
|
+
|
|
664
|
+
describe('DigitalObjectsFunctionRegistry - Edge Cases', () => {
|
|
665
|
+
let provider: DigitalObjectsProvider
|
|
666
|
+
let registry: DigitalObjectsFunctionRegistry
|
|
667
|
+
|
|
668
|
+
beforeEach(async () => {
|
|
669
|
+
provider = createMemoryProvider()
|
|
670
|
+
registry = await createDigitalObjectsRegistry({ provider })
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
it('handles updating an existing function', async () => {
|
|
674
|
+
const fn1 = defineFunction({
|
|
675
|
+
type: 'generative',
|
|
676
|
+
name: 'evolving',
|
|
677
|
+
args: { v: 'version 1' },
|
|
678
|
+
output: 'string',
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
await registry.setAsync('evolving', fn1)
|
|
682
|
+
|
|
683
|
+
// Update with new version
|
|
684
|
+
const fn2 = defineFunction({
|
|
685
|
+
type: 'generative',
|
|
686
|
+
name: 'evolving',
|
|
687
|
+
args: { v: 'version 2', extra: 'new field' },
|
|
688
|
+
output: 'string',
|
|
689
|
+
})
|
|
690
|
+
|
|
691
|
+
await registry.setAsync('evolving', fn2)
|
|
692
|
+
|
|
693
|
+
// Should still have only one Thing
|
|
694
|
+
const things = await provider.find(FUNCTION_NOUNS.GENERATIVE, { name: 'evolving' })
|
|
695
|
+
expect(things.length).toBe(1)
|
|
696
|
+
|
|
697
|
+
// Retrieved function should be the updated one
|
|
698
|
+
const retrieved = registry.get('evolving')
|
|
699
|
+
expect(retrieved).toBe(fn2)
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
it('handles multiple initializations gracefully', async () => {
|
|
703
|
+
// Initialize multiple times - should not throw or duplicate nouns
|
|
704
|
+
await registry.initialize()
|
|
705
|
+
await registry.initialize()
|
|
706
|
+
await registry.initialize()
|
|
707
|
+
|
|
708
|
+
const nouns = await provider.listNouns()
|
|
709
|
+
expect(nouns.length).toBe(4) // Should still be exactly 4
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
it('preserves function type-specific fields', async () => {
|
|
713
|
+
const humanFn = defineFunction({
|
|
714
|
+
type: 'human',
|
|
715
|
+
name: 'approval',
|
|
716
|
+
args: { request: 'Request details' },
|
|
717
|
+
channel: 'email',
|
|
718
|
+
instructions: 'Please review and approve',
|
|
719
|
+
timeout: 86400000,
|
|
720
|
+
assignee: 'manager@example.com',
|
|
721
|
+
})
|
|
722
|
+
|
|
723
|
+
await registry.setAsync('approval', humanFn)
|
|
724
|
+
|
|
725
|
+
// Retrieve from a fresh registry
|
|
726
|
+
const newRegistry = await createDigitalObjectsRegistry({ provider })
|
|
727
|
+
const retrieved = await newRegistry.getAsync('approval')
|
|
728
|
+
|
|
729
|
+
expect(retrieved?.definition.type).toBe('human')
|
|
730
|
+
const def = retrieved?.definition as {
|
|
731
|
+
channel: string
|
|
732
|
+
instructions: string
|
|
733
|
+
timeout: number
|
|
734
|
+
assignee: string
|
|
735
|
+
}
|
|
736
|
+
expect(def.channel).toBe('email')
|
|
737
|
+
expect(def.instructions).toBe('Please review and approve')
|
|
738
|
+
expect(def.timeout).toBe(86400000)
|
|
739
|
+
expect(def.assignee).toBe('manager@example.com')
|
|
740
|
+
})
|
|
741
|
+
|
|
742
|
+
it('tracks define action when creating new function', async () => {
|
|
743
|
+
const fn = defineFunction({
|
|
744
|
+
type: 'generative',
|
|
745
|
+
name: 'newFunc',
|
|
746
|
+
args: {},
|
|
747
|
+
output: 'string',
|
|
748
|
+
})
|
|
749
|
+
|
|
750
|
+
await registry.setAsync('newFunc', fn)
|
|
751
|
+
|
|
752
|
+
// Should have a define action
|
|
753
|
+
const actions = await provider.listActions({ verb: FUNCTION_VERBS.DEFINE })
|
|
754
|
+
expect(actions.length).toBe(1)
|
|
755
|
+
expect(actions[0].data).toMatchObject({
|
|
756
|
+
name: 'newFunc',
|
|
757
|
+
type: 'generative',
|
|
758
|
+
})
|
|
759
|
+
})
|
|
760
|
+
})
|