ai-functions 0.3.0 → 0.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 +5 -0
- package/.turbo/turbo-test.log +105 -0
- package/README.md +190 -86
- package/TODO.md +138 -0
- package/dist/ai-promise.d.ts +219 -0
- package/dist/ai-promise.d.ts.map +1 -0
- package/dist/ai-promise.js +610 -0
- package/dist/ai-promise.js.map +1 -0
- package/dist/ai.d.ts +285 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +842 -0
- package/dist/ai.js.map +1 -0
- package/dist/batch/anthropic.d.ts +23 -0
- package/dist/batch/anthropic.d.ts.map +1 -0
- package/dist/batch/anthropic.js +257 -0
- package/dist/batch/anthropic.js.map +1 -0
- package/dist/batch/bedrock.d.ts +64 -0
- package/dist/batch/bedrock.d.ts.map +1 -0
- package/dist/batch/bedrock.js +586 -0
- package/dist/batch/bedrock.js.map +1 -0
- package/dist/batch/cloudflare.d.ts +37 -0
- package/dist/batch/cloudflare.d.ts.map +1 -0
- package/dist/batch/cloudflare.js +289 -0
- package/dist/batch/cloudflare.js.map +1 -0
- package/dist/batch/google.d.ts +41 -0
- package/dist/batch/google.d.ts.map +1 -0
- package/dist/batch/google.js +360 -0
- package/dist/batch/google.js.map +1 -0
- package/dist/batch/index.d.ts +31 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/batch/index.js +31 -0
- package/dist/batch/index.js.map +1 -0
- package/dist/batch/memory.d.ts +44 -0
- package/dist/batch/memory.d.ts.map +1 -0
- package/dist/batch/memory.js +188 -0
- package/dist/batch/memory.js.map +1 -0
- package/dist/batch/openai.d.ts +37 -0
- package/dist/batch/openai.d.ts.map +1 -0
- package/dist/batch/openai.js +403 -0
- package/dist/batch/openai.js.map +1 -0
- package/dist/batch-map.d.ts +125 -0
- package/dist/batch-map.d.ts.map +1 -0
- package/dist/batch-map.js +406 -0
- package/dist/batch-map.js.map +1 -0
- package/dist/batch-queue.d.ts +273 -0
- package/dist/batch-queue.d.ts.map +1 -0
- package/dist/batch-queue.js +271 -0
- package/dist/batch-queue.js.map +1 -0
- package/dist/context.d.ts +133 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +267 -0
- package/dist/context.js.map +1 -0
- package/dist/embeddings.d.ts +123 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +170 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/eval/index.d.ts +8 -0
- package/dist/eval/index.d.ts.map +1 -0
- package/dist/eval/index.js +8 -0
- package/dist/eval/index.js.map +1 -0
- package/dist/eval/models.d.ts +66 -0
- package/dist/eval/models.d.ts.map +1 -0
- package/dist/eval/models.js +120 -0
- package/dist/eval/models.js.map +1 -0
- package/dist/eval/runner.d.ts +64 -0
- package/dist/eval/runner.d.ts.map +1 -0
- package/dist/eval/runner.js +148 -0
- package/dist/eval/runner.js.map +1 -0
- package/dist/generate.d.ts +168 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +174 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +29 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -52
- package/dist/index.js.map +1 -1
- package/dist/primitives.d.ts +292 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +471 -0
- package/dist/primitives.js.map +1 -0
- package/dist/providers/cloudflare.d.ts +9 -0
- package/dist/providers/cloudflare.d.ts.map +1 -0
- package/dist/providers/cloudflare.js +9 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/index.d.ts +9 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +9 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/schema.d.ts +54 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +109 -0
- package/dist/schema.js.map +1 -0
- package/dist/template.d.ts +73 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +129 -0
- package/dist/template.js.map +1 -0
- package/dist/types.d.ts +474 -106
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -8
- package/dist/types.js.map +1 -1
- package/evalite.config.ts +19 -0
- package/evals/README.md +212 -0
- package/evals/classification.eval.ts +108 -0
- package/evals/marketing.eval.ts +370 -0
- package/evals/math.eval.ts +94 -0
- package/evals/run-evals.ts +166 -0
- package/evals/structured-output.eval.ts +143 -0
- package/evals/writing.eval.ts +117 -0
- package/examples/batch-blog-posts.ts +160 -0
- package/package.json +57 -57
- package/src/ai-promise.ts +784 -0
- package/src/ai.ts +1183 -0
- package/src/batch/anthropic.ts +375 -0
- package/src/batch/bedrock.ts +801 -0
- package/src/batch/cloudflare.ts +421 -0
- package/src/batch/google.ts +491 -0
- package/src/batch/index.ts +31 -0
- package/src/batch/memory.ts +253 -0
- package/src/batch/openai.ts +557 -0
- package/src/batch-map.ts +534 -0
- package/src/batch-queue.ts +493 -0
- package/src/context.ts +332 -0
- package/src/embeddings.ts +244 -0
- package/src/eval/index.ts +8 -0
- package/src/eval/models.ts +158 -0
- package/src/eval/runner.ts +217 -0
- package/src/generate.ts +245 -0
- package/src/index.ts +154 -0
- package/src/primitives.ts +612 -0
- package/src/providers/cloudflare.ts +15 -0
- package/src/providers/index.ts +14 -0
- package/src/schema.ts +147 -0
- package/src/template.ts +209 -0
- package/src/types.ts +540 -0
- package/test/README.md +105 -0
- package/test/ai-proxy.test.ts +192 -0
- package/test/async-iterators.test.ts +327 -0
- package/test/batch-background.test.ts +482 -0
- package/test/batch-blog-posts.test.ts +387 -0
- package/test/blog-generation.test.ts +510 -0
- package/test/browse-read.test.ts +611 -0
- package/test/core-functions.test.ts +694 -0
- package/test/decide.test.ts +393 -0
- package/test/define.test.ts +274 -0
- package/test/e2e-bedrock-manual.ts +163 -0
- package/test/e2e-bedrock.test.ts +191 -0
- package/test/e2e-flex-gateway.ts +157 -0
- package/test/e2e-flex-manual.ts +183 -0
- package/test/e2e-flex.test.ts +209 -0
- package/test/e2e-google-manual.ts +178 -0
- package/test/e2e-google.test.ts +216 -0
- package/test/embeddings.test.ts +284 -0
- package/test/evals/define-function.eval.test.ts +379 -0
- package/test/evals/primitives.eval.test.ts +384 -0
- package/test/function-types.test.ts +492 -0
- package/test/generate-core.test.ts +319 -0
- package/test/generate.test.ts +163 -0
- package/test/implicit-batch.test.ts +422 -0
- package/test/schema.test.ts +109 -0
- package/test/tagged-templates.test.ts +302 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +42 -0
- package/LICENSE +0 -21
- package/bin/cli.js +0 -5
- package/dist/cli/index.d.ts +0 -10
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -38
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/index.test.d.ts +0 -2
- package/dist/cli/index.test.d.ts.map +0 -1
- package/dist/cli/index.test.js +0 -35
- package/dist/cli/index.test.js.map +0 -1
- package/dist/constants/models.d.ts +0 -10
- package/dist/constants/models.d.ts.map +0 -1
- package/dist/constants/models.js +0 -12
- package/dist/constants/models.js.map +0 -1
- package/dist/converters/index.d.ts +0 -3
- package/dist/converters/index.d.ts.map +0 -1
- package/dist/converters/index.js +0 -3
- package/dist/converters/index.js.map +0 -1
- package/dist/converters/model.d.ts +0 -4
- package/dist/converters/model.d.ts.map +0 -1
- package/dist/converters/model.js +0 -19
- package/dist/converters/model.js.map +0 -1
- package/dist/converters/schema.d.ts +0 -4
- package/dist/converters/schema.d.ts.map +0 -1
- package/dist/converters/schema.js +0 -25
- package/dist/converters/schema.js.map +0 -1
- package/dist/core/responses.d.ts +0 -5
- package/dist/core/responses.d.ts.map +0 -1
- package/dist/core/responses.js +0 -16
- package/dist/core/responses.js.map +0 -1
- package/dist/core/responses.test.d.ts +0 -2
- package/dist/core/responses.test.d.ts.map +0 -1
- package/dist/core/responses.test.js +0 -31
- package/dist/core/responses.test.js.map +0 -1
- package/dist/errors.d.ts +0 -6
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -9
- package/dist/errors.js.map +0 -1
- package/dist/examples/streaming.test.d.ts +0 -2
- package/dist/examples/streaming.test.d.ts.map +0 -1
- package/dist/examples/streaming.test.js +0 -176
- package/dist/examples/streaming.test.js.map +0 -1
- package/dist/factory/__tests__/index.test.d.ts +0 -2
- package/dist/factory/__tests__/index.test.d.ts.map +0 -1
- package/dist/factory/__tests__/index.test.js +0 -430
- package/dist/factory/__tests__/index.test.js.map +0 -1
- package/dist/factory/__tests__/list.test.d.ts +0 -2
- package/dist/factory/__tests__/list.test.d.ts.map +0 -1
- package/dist/factory/__tests__/list.test.js +0 -92
- package/dist/factory/__tests__/list.test.js.map +0 -1
- package/dist/factory/index.d.ts +0 -20
- package/dist/factory/index.d.ts.map +0 -1
- package/dist/factory/index.js +0 -287
- package/dist/factory/index.js.map +0 -1
- package/dist/factory/index.test.d.ts +0 -2
- package/dist/factory/index.test.d.ts.map +0 -1
- package/dist/factory/index.test.js +0 -287
- package/dist/factory/index.test.js.map +0 -1
- package/dist/factory/list.d.ts +0 -3
- package/dist/factory/list.d.ts.map +0 -1
- package/dist/factory/list.js +0 -221
- package/dist/factory/list.js.map +0 -1
- package/dist/factory/list.test.d.ts +0 -2
- package/dist/factory/list.test.d.ts.map +0 -1
- package/dist/factory/list.test.js +0 -84
- package/dist/factory/list.test.js.map +0 -1
- package/dist/generate/index.d.ts +0 -5
- package/dist/generate/index.d.ts.map +0 -1
- package/dist/generate/index.js +0 -17
- package/dist/generate/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -59
- package/dist/index.test.js.map +0 -1
- package/dist/list/await.d.ts +0 -3
- package/dist/list/await.d.ts.map +0 -1
- package/dist/list/await.js +0 -28
- package/dist/list/await.js.map +0 -1
- package/dist/list/constants.d.ts +0 -4
- package/dist/list/constants.d.ts.map +0 -1
- package/dist/list/constants.js +0 -5
- package/dist/list/constants.js.map +0 -1
- package/dist/list/create-function.d.ts +0 -3
- package/dist/list/create-function.d.ts.map +0 -1
- package/dist/list/create-function.js +0 -11
- package/dist/list/create-function.js.map +0 -1
- package/dist/list/index.d.ts +0 -4
- package/dist/list/index.d.ts.map +0 -1
- package/dist/list/index.js +0 -5
- package/dist/list/index.js.map +0 -1
- package/dist/list/prompt.d.ts +0 -3
- package/dist/list/prompt.d.ts.map +0 -1
- package/dist/list/prompt.js +0 -6
- package/dist/list/prompt.js.map +0 -1
- package/dist/list/schemas.d.ts +0 -4
- package/dist/list/schemas.d.ts.map +0 -1
- package/dist/list/schemas.js +0 -8
- package/dist/list/schemas.js.map +0 -1
- package/dist/list/stream.d.ts +0 -3
- package/dist/list/stream.d.ts.map +0 -1
- package/dist/list/stream.js +0 -33
- package/dist/list/stream.js.map +0 -1
- package/dist/list/types.d.ts +0 -11
- package/dist/list/types.d.ts.map +0 -1
- package/dist/list/types.js +0 -2
- package/dist/list/types.js.map +0 -1
- package/dist/list/validation.d.ts +0 -3
- package/dist/list/validation.d.ts.map +0 -1
- package/dist/list/validation.js +0 -12
- package/dist/list/validation.js.map +0 -1
- package/dist/providers/config.d.ts +0 -4
- package/dist/providers/config.d.ts.map +0 -1
- package/dist/providers/config.js +0 -21
- package/dist/providers/config.js.map +0 -1
- package/dist/providers/config.test.d.ts +0 -2
- package/dist/providers/config.test.d.ts.map +0 -1
- package/dist/providers/config.test.js +0 -37
- package/dist/providers/config.test.js.map +0 -1
- package/dist/proxy/constants.d.ts +0 -4
- package/dist/proxy/constants.d.ts.map +0 -1
- package/dist/proxy/constants.js +0 -5
- package/dist/proxy/constants.js.map +0 -1
- package/dist/proxy/create-function.d.ts +0 -4
- package/dist/proxy/create-function.d.ts.map +0 -1
- package/dist/proxy/create-function.js +0 -24
- package/dist/proxy/create-function.js.map +0 -1
- package/dist/proxy/create-proxy.d.ts +0 -2
- package/dist/proxy/create-proxy.d.ts.map +0 -1
- package/dist/proxy/create-proxy.js +0 -11
- package/dist/proxy/create-proxy.js.map +0 -1
- package/dist/proxy/function-generator.d.ts +0 -9
- package/dist/proxy/function-generator.d.ts.map +0 -1
- package/dist/proxy/function-generator.js +0 -29
- package/dist/proxy/function-generator.js.map +0 -1
- package/dist/proxy/index.d.ts +0 -4
- package/dist/proxy/index.d.ts.map +0 -1
- package/dist/proxy/index.js +0 -4
- package/dist/proxy/index.js.map +0 -1
- package/dist/proxy/prompt.d.ts +0 -2
- package/dist/proxy/prompt.d.ts.map +0 -1
- package/dist/proxy/prompt.js +0 -6
- package/dist/proxy/prompt.js.map +0 -1
- package/dist/proxy/types.d.ts +0 -7
- package/dist/proxy/types.d.ts.map +0 -1
- package/dist/proxy/types.js +0 -2
- package/dist/proxy/types.js.map +0 -1
- package/dist/queue/manager.d.ts +0 -5
- package/dist/queue/manager.d.ts.map +0 -1
- package/dist/queue/manager.js +0 -37
- package/dist/queue/manager.js.map +0 -1
- package/dist/queue/manager.test.d.ts +0 -2
- package/dist/queue/manager.test.d.ts.map +0 -1
- package/dist/queue/manager.test.js +0 -52
- package/dist/queue/manager.test.js.map +0 -1
- package/dist/schema-converter.d.ts +0 -4
- package/dist/schema-converter.d.ts.map +0 -1
- package/dist/schema-converter.js +0 -30
- package/dist/schema-converter.js.map +0 -1
- package/dist/stream/index.d.ts +0 -7
- package/dist/stream/index.d.ts.map +0 -1
- package/dist/stream/index.js +0 -23
- package/dist/stream/index.js.map +0 -1
- package/dist/streaming/utils.d.ts +0 -4
- package/dist/streaming/utils.d.ts.map +0 -1
- package/dist/streaming/utils.js +0 -131
- package/dist/streaming/utils.js.map +0 -1
- package/dist/streaming/utils.test.d.ts +0 -2
- package/dist/streaming/utils.test.d.ts.map +0 -1
- package/dist/streaming/utils.test.js +0 -84
- package/dist/streaming/utils.test.js.map +0 -1
- package/dist/templates/result.d.ts +0 -7
- package/dist/templates/result.d.ts.map +0 -1
- package/dist/templates/result.js +0 -40
- package/dist/templates/result.js.map +0 -1
- package/dist/templates/result.test.d.ts +0 -2
- package/dist/templates/result.test.d.ts.map +0 -1
- package/dist/templates/result.test.js +0 -75
- package/dist/templates/result.test.js.map +0 -1
- package/dist/test/setup.d.ts +0 -2
- package/dist/test/setup.d.ts.map +0 -1
- package/dist/test/setup.js +0 -21
- package/dist/test/setup.js.map +0 -1
- package/dist/test-types.d.ts +0 -13
- package/dist/test-types.d.ts.map +0 -1
- package/dist/test-types.js +0 -55
- package/dist/test-types.js.map +0 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +0 -1
- package/dist/types/list.d.ts +0 -10
- package/dist/types/list.d.ts.map +0 -1
- package/dist/types/list.js +0 -2
- package/dist/types/list.js.map +0 -1
- package/dist/types/model.d.ts +0 -7
- package/dist/types/model.d.ts.map +0 -1
- package/dist/types/model.js +0 -2
- package/dist/types/model.js.map +0 -1
- package/dist/types/options.d.ts +0 -25
- package/dist/types/options.d.ts.map +0 -1
- package/dist/types/options.js +0 -2
- package/dist/types/options.js.map +0 -1
- package/dist/types/schema.d.ts +0 -5
- package/dist/types/schema.d.ts.map +0 -1
- package/dist/types/schema.js +0 -2
- package/dist/types/schema.js.map +0 -1
- package/dist/utils/__tests__/request-handler.test.d.ts +0 -2
- package/dist/utils/__tests__/request-handler.test.d.ts.map +0 -1
- package/dist/utils/__tests__/request-handler.test.js +0 -134
- package/dist/utils/__tests__/request-handler.test.js.map +0 -1
- package/dist/utils/__tests__/schema.test.d.ts +0 -2
- package/dist/utils/__tests__/schema.test.d.ts.map +0 -1
- package/dist/utils/__tests__/schema.test.js +0 -49
- package/dist/utils/__tests__/schema.test.js.map +0 -1
- package/dist/utils/__tests__/stream-progress.test.d.ts +0 -2
- package/dist/utils/__tests__/stream-progress.test.d.ts.map +0 -1
- package/dist/utils/__tests__/stream-progress.test.js +0 -85
- package/dist/utils/__tests__/stream-progress.test.js.map +0 -1
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -2
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/request-handler.d.ts +0 -17
- package/dist/utils/request-handler.d.ts.map +0 -1
- package/dist/utils/request-handler.js +0 -105
- package/dist/utils/request-handler.js.map +0 -1
- package/dist/utils/schema.d.ts +0 -11
- package/dist/utils/schema.d.ts.map +0 -1
- package/dist/utils/schema.js +0 -51
- package/dist/utils/schema.js.map +0 -1
- package/dist/utils/stream-progress.d.ts +0 -17
- package/dist/utils/stream-progress.d.ts.map +0 -1
- package/dist/utils/stream-progress.js +0 -86
- package/dist/utils/stream-progress.js.map +0 -1
- package/dist/utils/validation.d.ts +0 -3
- package/dist/utils/validation.d.ts.map +0 -1
- package/dist/utils/validation.js +0 -30
- package/dist/utils/validation.js.map +0 -1
package/src/ai.ts
ADDED
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI() and ai() - Core AI function constructors
|
|
3
|
+
*
|
|
4
|
+
* These provide the main entry points for AI-powered functions,
|
|
5
|
+
* with full RPC promise pipelining support via rpc.do.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { RPC, http, ws, type RPCProxy, type RPCPromise as RpcPromiseType } from 'rpc.do'
|
|
9
|
+
|
|
10
|
+
// Use Promise as RpcPromise for type definitions
|
|
11
|
+
// The actual RPC layer handles pipelining transparently
|
|
12
|
+
type RpcPromise<T> = Promise<T>
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base class for RPC service targets
|
|
16
|
+
* This is a placeholder for services that want to expose methods over RPC
|
|
17
|
+
*/
|
|
18
|
+
export abstract class RpcTarget {
|
|
19
|
+
// Subclasses implement methods that will be exposed over RPC
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for RPC session (connection to remote AI service)
|
|
24
|
+
*/
|
|
25
|
+
export interface RPCSessionOptions {
|
|
26
|
+
/** WebSocket URL for RPC connection */
|
|
27
|
+
wsUrl?: string
|
|
28
|
+
/** HTTP URL for RPC fallback */
|
|
29
|
+
httpUrl?: string
|
|
30
|
+
/** Authentication token */
|
|
31
|
+
token?: string
|
|
32
|
+
}
|
|
33
|
+
import { generateObject } from './generate.js'
|
|
34
|
+
import type { SimpleSchema } from './schema.js'
|
|
35
|
+
import type { LanguageModel } from 'ai'
|
|
36
|
+
import type {
|
|
37
|
+
AIClient,
|
|
38
|
+
AIFunctionDefinition,
|
|
39
|
+
AIGenerateOptions,
|
|
40
|
+
AIGenerateResult,
|
|
41
|
+
JSONSchema,
|
|
42
|
+
ImageOptions,
|
|
43
|
+
ImageResult,
|
|
44
|
+
VideoOptions,
|
|
45
|
+
VideoResult,
|
|
46
|
+
WriteOptions,
|
|
47
|
+
ListResult,
|
|
48
|
+
ListsResult,
|
|
49
|
+
FunctionDefinition,
|
|
50
|
+
DefinedFunction,
|
|
51
|
+
CodeFunctionDefinition,
|
|
52
|
+
CodeFunctionResult,
|
|
53
|
+
GenerativeFunctionDefinition,
|
|
54
|
+
AgenticFunctionDefinition,
|
|
55
|
+
HumanFunctionDefinition,
|
|
56
|
+
HumanChannel,
|
|
57
|
+
CodeLanguage,
|
|
58
|
+
FunctionRegistry,
|
|
59
|
+
AutoDefineResult
|
|
60
|
+
} from './types.js'
|
|
61
|
+
import { schema as convertSchema, type SimpleSchema as SimpleSchemaType } from './schema.js'
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Options for creating an AI RPC client instance
|
|
65
|
+
*/
|
|
66
|
+
export interface AIClientOptions extends RPCSessionOptions {
|
|
67
|
+
/** Default model to use */
|
|
68
|
+
model?: string
|
|
69
|
+
/** Default temperature */
|
|
70
|
+
temperature?: number
|
|
71
|
+
/** Default max tokens */
|
|
72
|
+
maxTokens?: number
|
|
73
|
+
/** Custom functions available to the AI */
|
|
74
|
+
functions?: AIFunctionDefinition[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Options for AI schema functions (subset of generateObject options)
|
|
79
|
+
*/
|
|
80
|
+
export interface AISchemaOptions {
|
|
81
|
+
/** Model to use (string alias or LanguageModel) */
|
|
82
|
+
model?: string | LanguageModel
|
|
83
|
+
/** System prompt */
|
|
84
|
+
system?: string
|
|
85
|
+
/** Generation mode */
|
|
86
|
+
mode?: 'auto' | 'json' | 'tool'
|
|
87
|
+
/** Temperature (0-2) */
|
|
88
|
+
temperature?: number
|
|
89
|
+
/** Top P sampling */
|
|
90
|
+
topP?: number
|
|
91
|
+
/** Top K sampling */
|
|
92
|
+
topK?: number
|
|
93
|
+
/** Presence penalty */
|
|
94
|
+
presencePenalty?: number
|
|
95
|
+
/** Frequency penalty */
|
|
96
|
+
frequencyPenalty?: number
|
|
97
|
+
/** Max tokens to generate */
|
|
98
|
+
maxTokens?: number
|
|
99
|
+
/** Max retries on failure */
|
|
100
|
+
maxRetries?: number
|
|
101
|
+
/** Abort signal */
|
|
102
|
+
abortSignal?: AbortSignal
|
|
103
|
+
/** Custom headers */
|
|
104
|
+
headers?: Record<string, string>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Schema-based functions
|
|
109
|
+
*/
|
|
110
|
+
type SchemaFunctions<T extends Record<string, SimpleSchema>> = {
|
|
111
|
+
[K in keyof T]: (prompt?: string, options?: AISchemaOptions) => Promise<InferSimpleSchemaResult<T[K]>>
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create AI functions from schemas
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* const ai = AI({
|
|
120
|
+
* storyBrand: {
|
|
121
|
+
* hero: 'Who is the customer?',
|
|
122
|
+
* problem: {
|
|
123
|
+
* internal: 'What internal problem do they face?',
|
|
124
|
+
* external: 'What external challenge exists?',
|
|
125
|
+
* philosophical: 'Why is this wrong?',
|
|
126
|
+
* },
|
|
127
|
+
* guide: 'Who helps them? (the brand)',
|
|
128
|
+
* plan: ['What are the steps to success?'],
|
|
129
|
+
* callToAction: 'What should they do?',
|
|
130
|
+
* success: 'What does success look like?',
|
|
131
|
+
* failure: 'What happens if they fail?',
|
|
132
|
+
* },
|
|
133
|
+
* recipe: {
|
|
134
|
+
* name: 'Recipe name',
|
|
135
|
+
* type: 'food | drink | dessert',
|
|
136
|
+
* servings: 'How many servings? (number)',
|
|
137
|
+
* ingredients: ['List all ingredients'],
|
|
138
|
+
* steps: ['Cooking steps in order'],
|
|
139
|
+
* },
|
|
140
|
+
* })
|
|
141
|
+
*
|
|
142
|
+
* // Call the generated functions
|
|
143
|
+
* const brand = await ai.storyBrand('Acme Corp sells widgets')
|
|
144
|
+
* const dinner = await ai.recipe('Italian pasta for 4 people')
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export function AI<T extends Record<string, SimpleSchema>>(
|
|
148
|
+
schemas: T,
|
|
149
|
+
defaultOptions?: AISchemaOptions
|
|
150
|
+
): SchemaFunctions<T>
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create an AI RPC client instance
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* // Connect to remote AI service
|
|
158
|
+
* const ai = AI({ wsUrl: 'wss://ai.example.com/rpc' })
|
|
159
|
+
*
|
|
160
|
+
* // Use promise pipelining - single round trip!
|
|
161
|
+
* const result = ai.generate({ prompt: 'Hello' })
|
|
162
|
+
* const upper = result.text.map(t => t.toUpperCase())
|
|
163
|
+
* console.log(await upper)
|
|
164
|
+
*
|
|
165
|
+
* // Dynamic function calling
|
|
166
|
+
* const summary = await ai.summarize({ text: longText })
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export function AI(options: AIClientOptions): AIClient & Record<string, (...args: unknown[]) => RpcPromise<unknown>>
|
|
170
|
+
|
|
171
|
+
export function AI<T extends Record<string, SimpleSchema>>(
|
|
172
|
+
schemasOrOptions: T | AIClientOptions,
|
|
173
|
+
defaultOptions?: AISchemaOptions
|
|
174
|
+
): SchemaFunctions<T> | (AIClient & Record<string, (...args: unknown[]) => RpcPromise<unknown>>) {
|
|
175
|
+
// Check if this is RPC client mode
|
|
176
|
+
if (isAIClientOptions(schemasOrOptions)) {
|
|
177
|
+
const { model, temperature, maxTokens, functions, wsUrl, httpUrl, token } = schemasOrOptions
|
|
178
|
+
|
|
179
|
+
// Create transport based on provided URLs
|
|
180
|
+
let transport
|
|
181
|
+
if (wsUrl) {
|
|
182
|
+
transport = ws(wsUrl, token ? () => token : undefined)
|
|
183
|
+
} else if (httpUrl) {
|
|
184
|
+
transport = http(httpUrl, token ? () => token : undefined)
|
|
185
|
+
} else {
|
|
186
|
+
throw new Error('AI client requires either wsUrl or httpUrl')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Create RPC client
|
|
190
|
+
const rpcClient = RPC<AIClient>(transport)
|
|
191
|
+
|
|
192
|
+
// Create a proxy that handles both defined methods and dynamic function calls
|
|
193
|
+
return new Proxy(rpcClient, {
|
|
194
|
+
get(target, prop: string) {
|
|
195
|
+
// Return existing methods
|
|
196
|
+
if (prop in target) {
|
|
197
|
+
return (target as unknown as Record<string, unknown>)[prop]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Handle dynamic function calls (ai.functionName())
|
|
201
|
+
return (...args: unknown[]) => {
|
|
202
|
+
const client = target as unknown as AIClient
|
|
203
|
+
return client.do(prop, args.length === 1 ? args[0] : args)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}) as AIClient & Record<string, (...args: unknown[]) => RpcPromise<unknown>>
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Schema functions mode - create a function for each schema
|
|
210
|
+
return createSchemaFunctions(schemasOrOptions as Record<string, SimpleSchema>, defaultOptions) as SchemaFunctions<T>
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if options are AI client options vs schemas
|
|
215
|
+
*/
|
|
216
|
+
function isAIClientOptions(value: unknown): value is AIClientOptions {
|
|
217
|
+
if (typeof value !== 'object' || value === null) return false
|
|
218
|
+
const obj = value as Record<string, unknown>
|
|
219
|
+
return 'wsUrl' in obj || 'httpUrl' in obj || 'functions' in obj
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Infer result type from simple schema
|
|
224
|
+
*/
|
|
225
|
+
type InferSimpleSchemaResult<T> = T extends string
|
|
226
|
+
? string
|
|
227
|
+
: T extends [string]
|
|
228
|
+
? string[]
|
|
229
|
+
: T extends { [K: string]: SimpleSchema }
|
|
230
|
+
? { [K in keyof T]: InferSimpleSchemaResult<T[K]> }
|
|
231
|
+
: unknown
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create schema-based functions from a map of schemas
|
|
235
|
+
*/
|
|
236
|
+
function createSchemaFunctions<T extends Record<string, SimpleSchema>>(
|
|
237
|
+
schemas: T,
|
|
238
|
+
defaultOptions: AISchemaOptions = {}
|
|
239
|
+
): SchemaFunctions<T> {
|
|
240
|
+
const functions: Record<string, (prompt?: string, options?: AISchemaOptions) => Promise<unknown>> = {}
|
|
241
|
+
|
|
242
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
243
|
+
functions[name] = async (prompt?: string, options?: AISchemaOptions) => {
|
|
244
|
+
const mergedOptions = { ...defaultOptions, ...options }
|
|
245
|
+
const { model = 'sonnet', system, ...rest } = mergedOptions
|
|
246
|
+
|
|
247
|
+
// Build prompt from schema descriptions if none provided
|
|
248
|
+
const schemaPrompt = prompt || buildPromptFromSchema(schema)
|
|
249
|
+
|
|
250
|
+
const result = await generateObject({
|
|
251
|
+
model,
|
|
252
|
+
schema,
|
|
253
|
+
prompt: schemaPrompt,
|
|
254
|
+
system,
|
|
255
|
+
...rest,
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
return result.object
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return functions as SchemaFunctions<T>
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Build a prompt by extracting descriptions from the schema
|
|
267
|
+
*/
|
|
268
|
+
function buildPromptFromSchema(schema: SimpleSchema, path = ''): string {
|
|
269
|
+
if (typeof schema === 'string') {
|
|
270
|
+
return schema
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (Array.isArray(schema)) {
|
|
274
|
+
return schema[0] as string || 'Generate items'
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (typeof schema === 'object' && schema !== null) {
|
|
278
|
+
const descriptions: string[] = []
|
|
279
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
280
|
+
const subPrompt = buildPromptFromSchema(value as SimpleSchema, path ? `${path}.${key}` : key)
|
|
281
|
+
if (subPrompt) {
|
|
282
|
+
descriptions.push(`${key}: ${subPrompt}`)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return descriptions.length > 0 ? `Generate the following:\n${descriptions.join('\n')}` : ''
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return ''
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Create a defined function from a function definition
|
|
293
|
+
*/
|
|
294
|
+
function createDefinedFunction<TArgs, TReturn>(
|
|
295
|
+
definition: FunctionDefinition<TArgs, TReturn>
|
|
296
|
+
): DefinedFunction<TArgs, TReturn> {
|
|
297
|
+
const call = async (args: TArgs): Promise<TReturn> => {
|
|
298
|
+
switch (definition.type) {
|
|
299
|
+
case 'code':
|
|
300
|
+
return executeCodeFunction(definition, args) as Promise<TReturn>
|
|
301
|
+
case 'generative':
|
|
302
|
+
return executeGenerativeFunction(definition, args) as Promise<TReturn>
|
|
303
|
+
case 'agentic':
|
|
304
|
+
return executeAgenticFunction(definition, args) as Promise<TReturn>
|
|
305
|
+
case 'human':
|
|
306
|
+
return executeHumanFunction(definition, args) as Promise<TReturn>
|
|
307
|
+
default:
|
|
308
|
+
throw new Error(`Unknown function type: ${(definition as FunctionDefinition).type}`)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const asTool = (): AIFunctionDefinition<TArgs, TReturn> => {
|
|
313
|
+
return {
|
|
314
|
+
name: definition.name,
|
|
315
|
+
description: definition.description || `Execute ${definition.name}`,
|
|
316
|
+
parameters: convertArgsToJSONSchema(definition.args),
|
|
317
|
+
handler: call,
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return { definition, call, asTool }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Convert args schema to JSON Schema
|
|
326
|
+
*/
|
|
327
|
+
function convertArgsToJSONSchema(args: unknown): JSONSchema {
|
|
328
|
+
// If it's already a JSON schema-like object
|
|
329
|
+
if (typeof args === 'object' && args !== null && 'type' in args) {
|
|
330
|
+
return args as JSONSchema
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Convert SimpleSchema to JSON Schema
|
|
334
|
+
const properties: Record<string, JSONSchema> = {}
|
|
335
|
+
const required: string[] = []
|
|
336
|
+
|
|
337
|
+
if (typeof args === 'object' && args !== null) {
|
|
338
|
+
for (const [key, value] of Object.entries(args as Record<string, unknown>)) {
|
|
339
|
+
required.push(key) // All properties required for cross-provider compatibility
|
|
340
|
+
properties[key] = convertValueToJSONSchema(value)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
type: 'object',
|
|
346
|
+
properties,
|
|
347
|
+
required,
|
|
348
|
+
additionalProperties: false, // Required for OpenAI compatibility
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Convert a single value to JSON Schema
|
|
354
|
+
*/
|
|
355
|
+
function convertValueToJSONSchema(value: unknown): JSONSchema {
|
|
356
|
+
if (typeof value === 'string') {
|
|
357
|
+
// Check for type hints: 'description (number)', 'description (boolean)', etc.
|
|
358
|
+
const typeMatch = value.match(/^(.+?)\s*\((number|boolean|integer|date)\)$/i)
|
|
359
|
+
if (typeMatch) {
|
|
360
|
+
const description = typeMatch[1]!
|
|
361
|
+
const type = typeMatch[2]!
|
|
362
|
+
switch (type.toLowerCase()) {
|
|
363
|
+
case 'number':
|
|
364
|
+
return { type: 'number', description: description.trim() }
|
|
365
|
+
case 'integer':
|
|
366
|
+
return { type: 'integer', description: description.trim() }
|
|
367
|
+
case 'boolean':
|
|
368
|
+
return { type: 'boolean', description: description.trim() }
|
|
369
|
+
case 'date':
|
|
370
|
+
return { type: 'string', format: 'date-time', description: description.trim() }
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Check for enum: 'option1 | option2 | option3'
|
|
375
|
+
if (value.includes(' | ')) {
|
|
376
|
+
const options = value.split(' | ').map(s => s.trim())
|
|
377
|
+
return { type: 'string', enum: options }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return { type: 'string', description: value }
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (Array.isArray(value) && value.length === 1) {
|
|
384
|
+
const [desc] = value
|
|
385
|
+
if (typeof desc === 'string') {
|
|
386
|
+
return { type: 'array', items: { type: 'string' }, description: desc }
|
|
387
|
+
}
|
|
388
|
+
if (typeof desc === 'number') {
|
|
389
|
+
return { type: 'array', items: { type: 'number' } }
|
|
390
|
+
}
|
|
391
|
+
return { type: 'array', items: convertValueToJSONSchema(desc) }
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (typeof value === 'object' && value !== null) {
|
|
395
|
+
return convertArgsToJSONSchema(value)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return { type: 'string' }
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Fill template with values
|
|
403
|
+
*/
|
|
404
|
+
function fillTemplate(template: string, args: Record<string, unknown>): string {
|
|
405
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => String(args[key] ?? ''))
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Execute a code function - generates code with tests and examples
|
|
410
|
+
*/
|
|
411
|
+
async function executeCodeFunction<TArgs>(
|
|
412
|
+
definition: CodeFunctionDefinition<TArgs>,
|
|
413
|
+
args: TArgs
|
|
414
|
+
): Promise<CodeFunctionResult> {
|
|
415
|
+
const { name, description, language = 'typescript', instructions, includeTests = true, includeExamples = true } = definition
|
|
416
|
+
|
|
417
|
+
const argsDescription = JSON.stringify(args, null, 2)
|
|
418
|
+
|
|
419
|
+
const result = await generateObject({
|
|
420
|
+
model: 'sonnet',
|
|
421
|
+
schema: {
|
|
422
|
+
code: 'The complete implementation code with JSDoc comments',
|
|
423
|
+
tests: includeTests ? 'Vitest test code for the implementation' : undefined,
|
|
424
|
+
examples: includeExamples ? 'Example usage code' : undefined,
|
|
425
|
+
documentation: 'JSDoc or equivalent documentation string',
|
|
426
|
+
},
|
|
427
|
+
system: `You are an expert ${language} developer. Generate clean, well-documented, production-ready code.`,
|
|
428
|
+
prompt: `Generate a ${language} function with the following specification:
|
|
429
|
+
|
|
430
|
+
Name: ${name}
|
|
431
|
+
Description: ${description || 'No description provided'}
|
|
432
|
+
Arguments: ${argsDescription}
|
|
433
|
+
Return Type: ${JSON.stringify(definition.returnType)}
|
|
434
|
+
|
|
435
|
+
${instructions ? `Additional Instructions: ${instructions}` : ''}
|
|
436
|
+
|
|
437
|
+
Requirements:
|
|
438
|
+
- Include comprehensive JSDoc comments
|
|
439
|
+
- Follow best practices for ${language}
|
|
440
|
+
- Handle edge cases appropriately
|
|
441
|
+
${includeTests ? '- Include vitest tests that cover main functionality and edge cases' : ''}
|
|
442
|
+
${includeExamples ? '- Include example usage showing how to call the function' : ''}`,
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
const obj = result.object as { code: string; tests?: string; examples?: string; documentation: string }
|
|
446
|
+
return {
|
|
447
|
+
code: obj.code,
|
|
448
|
+
tests: obj.tests,
|
|
449
|
+
examples: obj.examples,
|
|
450
|
+
language,
|
|
451
|
+
documentation: obj.documentation,
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Execute a generative function - uses AI to generate content
|
|
457
|
+
*/
|
|
458
|
+
async function executeGenerativeFunction<TArgs, TReturn>(
|
|
459
|
+
definition: GenerativeFunctionDefinition<TArgs, TReturn>,
|
|
460
|
+
args: TArgs
|
|
461
|
+
): Promise<TReturn> {
|
|
462
|
+
const { output, system, promptTemplate, model = 'sonnet', temperature, returnType } = definition
|
|
463
|
+
|
|
464
|
+
const prompt = promptTemplate
|
|
465
|
+
? fillTemplate(promptTemplate, args as Record<string, unknown>)
|
|
466
|
+
: JSON.stringify(args)
|
|
467
|
+
|
|
468
|
+
switch (output) {
|
|
469
|
+
case 'string': {
|
|
470
|
+
const result = await generateObject({
|
|
471
|
+
model,
|
|
472
|
+
schema: { text: 'The generated text response' },
|
|
473
|
+
system,
|
|
474
|
+
prompt,
|
|
475
|
+
temperature,
|
|
476
|
+
})
|
|
477
|
+
return (result.object as { text: string }).text as TReturn
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
case 'object': {
|
|
481
|
+
const objectSchema = returnType || { result: 'The generated result' }
|
|
482
|
+
const result = await generateObject({
|
|
483
|
+
model,
|
|
484
|
+
schema: objectSchema as SimpleSchemaType,
|
|
485
|
+
system,
|
|
486
|
+
prompt,
|
|
487
|
+
temperature,
|
|
488
|
+
})
|
|
489
|
+
return result.object as TReturn
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
case 'image': {
|
|
493
|
+
const client = getDefaultAIClient()
|
|
494
|
+
return client.image(prompt) as unknown as Promise<TReturn>
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
case 'video': {
|
|
498
|
+
const client = getDefaultAIClient()
|
|
499
|
+
return client.video(prompt) as unknown as Promise<TReturn>
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
case 'audio': {
|
|
503
|
+
// Audio generation would need a specific implementation
|
|
504
|
+
throw new Error('Audio generation not yet implemented')
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
default:
|
|
508
|
+
throw new Error(`Unknown output type: ${output}`)
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Execute an agentic function - runs in a loop with tools
|
|
514
|
+
*/
|
|
515
|
+
async function executeAgenticFunction<TArgs, TReturn>(
|
|
516
|
+
definition: AgenticFunctionDefinition<TArgs, TReturn>,
|
|
517
|
+
args: TArgs
|
|
518
|
+
): Promise<TReturn> {
|
|
519
|
+
const { instructions, promptTemplate, tools = [], maxIterations = 10, model = 'sonnet', returnType } = definition
|
|
520
|
+
|
|
521
|
+
const prompt = promptTemplate
|
|
522
|
+
? fillTemplate(promptTemplate, args as Record<string, unknown>)
|
|
523
|
+
: JSON.stringify(args)
|
|
524
|
+
|
|
525
|
+
// Build system prompt with tool descriptions
|
|
526
|
+
const toolDescriptions = tools.map(t => `- ${t.name}: ${t.description}`).join('\n')
|
|
527
|
+
const systemPrompt = `${instructions}
|
|
528
|
+
|
|
529
|
+
Available tools:
|
|
530
|
+
${toolDescriptions || 'No tools available'}
|
|
531
|
+
|
|
532
|
+
Work step by step to accomplish the task. When you have completed the task, provide your final result.`
|
|
533
|
+
|
|
534
|
+
let iteration = 0
|
|
535
|
+
const toolResults: unknown[] = []
|
|
536
|
+
|
|
537
|
+
// Simple agent loop
|
|
538
|
+
while (iteration < maxIterations) {
|
|
539
|
+
iteration++
|
|
540
|
+
|
|
541
|
+
const result = await generateObject({
|
|
542
|
+
model,
|
|
543
|
+
schema: {
|
|
544
|
+
thinking: 'Your step-by-step reasoning',
|
|
545
|
+
toolCall: {
|
|
546
|
+
name: 'Tool to call (or "done" if finished)',
|
|
547
|
+
arguments: 'Arguments for the tool as JSON string',
|
|
548
|
+
},
|
|
549
|
+
finalResult: returnType || 'The final result if done',
|
|
550
|
+
},
|
|
551
|
+
system: systemPrompt,
|
|
552
|
+
prompt: `Task: ${prompt}
|
|
553
|
+
|
|
554
|
+
Previous tool results:
|
|
555
|
+
${toolResults.map((r, i) => `Step ${i + 1}: ${JSON.stringify(r)}`).join('\n') || 'None yet'}
|
|
556
|
+
|
|
557
|
+
What is your next step?`,
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
const response = result.object as {
|
|
561
|
+
thinking: string
|
|
562
|
+
toolCall: { name: string; arguments: string }
|
|
563
|
+
finalResult: unknown
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (response.toolCall.name === 'done' || response.finalResult) {
|
|
567
|
+
return response.finalResult as TReturn
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Execute tool call
|
|
571
|
+
const tool = tools.find(t => t.name === response.toolCall.name)
|
|
572
|
+
if (tool) {
|
|
573
|
+
const toolArgs = JSON.parse(response.toolCall.arguments || '{}')
|
|
574
|
+
const toolResult = await tool.handler(toolArgs)
|
|
575
|
+
toolResults.push({ tool: response.toolCall.name, result: toolResult })
|
|
576
|
+
} else {
|
|
577
|
+
toolResults.push({ error: `Tool not found: ${response.toolCall.name}` })
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
throw new Error(`Agent exceeded maximum iterations (${maxIterations})`)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Execute a human function - generates UI and waits for human input
|
|
586
|
+
*/
|
|
587
|
+
async function executeHumanFunction<TArgs, TReturn>(
|
|
588
|
+
definition: HumanFunctionDefinition<TArgs, TReturn>,
|
|
589
|
+
args: TArgs
|
|
590
|
+
): Promise<TReturn> {
|
|
591
|
+
const { channel, instructions, promptTemplate, returnType } = definition
|
|
592
|
+
|
|
593
|
+
const prompt = promptTemplate
|
|
594
|
+
? fillTemplate(promptTemplate, args as Record<string, unknown>)
|
|
595
|
+
: JSON.stringify(args)
|
|
596
|
+
|
|
597
|
+
// Generate channel-specific UI
|
|
598
|
+
const uiSchema: Record<string, SimpleSchemaType> = {
|
|
599
|
+
// New HumanChannel types
|
|
600
|
+
chat: {
|
|
601
|
+
message: 'Chat message to send',
|
|
602
|
+
options: ['Response options if applicable'],
|
|
603
|
+
},
|
|
604
|
+
email: {
|
|
605
|
+
subject: 'Email subject',
|
|
606
|
+
html: 'Email HTML body',
|
|
607
|
+
text: 'Plain text fallback',
|
|
608
|
+
},
|
|
609
|
+
phone: {
|
|
610
|
+
script: 'Phone call script',
|
|
611
|
+
keyPoints: ['Key points to cover'],
|
|
612
|
+
},
|
|
613
|
+
sms: {
|
|
614
|
+
text: 'SMS message text (max 160 chars)',
|
|
615
|
+
},
|
|
616
|
+
workspace: {
|
|
617
|
+
blocks: ['Workspace/Slack BlockKit blocks as JSON array'],
|
|
618
|
+
text: 'Plain text fallback',
|
|
619
|
+
},
|
|
620
|
+
web: {
|
|
621
|
+
component: 'React component code for the form',
|
|
622
|
+
schema: 'JSON schema for the form fields',
|
|
623
|
+
},
|
|
624
|
+
// Legacy fallback
|
|
625
|
+
custom: {
|
|
626
|
+
data: 'Structured data for custom implementation',
|
|
627
|
+
instructions: 'Instructions for the human',
|
|
628
|
+
},
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const result = await generateObject({
|
|
632
|
+
model: 'sonnet',
|
|
633
|
+
schema: uiSchema[channel] ?? uiSchema.custom,
|
|
634
|
+
system: `Generate ${channel} UI/content for a human-in-the-loop task.`,
|
|
635
|
+
prompt: `Task: ${instructions}
|
|
636
|
+
|
|
637
|
+
Input data:
|
|
638
|
+
${prompt}
|
|
639
|
+
|
|
640
|
+
Expected response format:
|
|
641
|
+
${JSON.stringify(returnType)}
|
|
642
|
+
|
|
643
|
+
Generate the appropriate ${channel} UI/content to collect this response from a human.`,
|
|
644
|
+
})
|
|
645
|
+
|
|
646
|
+
// In a real implementation, this would:
|
|
647
|
+
// 1. Send the generated UI to the appropriate channel
|
|
648
|
+
// 2. Wait for human response
|
|
649
|
+
// 3. Return the validated response
|
|
650
|
+
|
|
651
|
+
// For now, return the generated artifacts as a placeholder
|
|
652
|
+
return {
|
|
653
|
+
_pending: true,
|
|
654
|
+
channel,
|
|
655
|
+
artifacts: result.object,
|
|
656
|
+
expectedResponseType: returnType,
|
|
657
|
+
} as unknown as TReturn
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Helper to create a function that supports both regular calls and tagged template literals
|
|
662
|
+
* @example
|
|
663
|
+
* const fn = withTemplate((prompt) => doSomething(prompt))
|
|
664
|
+
* fn('hello') // regular call
|
|
665
|
+
* fn`hello ${name}` // tagged template literal
|
|
666
|
+
*/
|
|
667
|
+
export function withTemplate<TArgs extends unknown[], TReturn>(
|
|
668
|
+
fn: (prompt: string, ...args: TArgs) => TReturn
|
|
669
|
+
): ((prompt: string, ...args: TArgs) => TReturn) & ((strings: TemplateStringsArray, ...values: unknown[]) => TReturn) {
|
|
670
|
+
return function (promptOrStrings: string | TemplateStringsArray, ...args: unknown[]): TReturn {
|
|
671
|
+
if (Array.isArray(promptOrStrings) && 'raw' in promptOrStrings) {
|
|
672
|
+
// Tagged template literal call - pass empty args for optional params
|
|
673
|
+
const strings = promptOrStrings as TemplateStringsArray
|
|
674
|
+
const values = args
|
|
675
|
+
const prompt = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ''), '')
|
|
676
|
+
return fn(prompt, ...([] as unknown as TArgs))
|
|
677
|
+
}
|
|
678
|
+
// Regular function call
|
|
679
|
+
return fn(promptOrStrings as string, ...(args as TArgs))
|
|
680
|
+
} as ((prompt: string, ...args: TArgs) => TReturn) & ((strings: TemplateStringsArray, ...values: unknown[]) => TReturn)
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// Default client management
|
|
684
|
+
let defaultClient: AIClient | null = null
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Configure the default AI client
|
|
688
|
+
*/
|
|
689
|
+
export function configureAI(options: AIClientOptions): void {
|
|
690
|
+
defaultClient = AI(options) as AIClient
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Get the default AI client, throwing if not configured
|
|
695
|
+
*/
|
|
696
|
+
function getDefaultAIClient(): AIClient {
|
|
697
|
+
if (!defaultClient) {
|
|
698
|
+
// Try to auto-configure from environment
|
|
699
|
+
const wsUrl = typeof process !== 'undefined' ? process.env?.AI_WS_URL : undefined
|
|
700
|
+
const httpUrl = typeof process !== 'undefined' ? process.env?.AI_HTTP_URL : undefined
|
|
701
|
+
|
|
702
|
+
if (wsUrl) {
|
|
703
|
+
defaultClient = AI({ wsUrl } as AIClientOptions) as unknown as AIClient
|
|
704
|
+
} else if (httpUrl) {
|
|
705
|
+
defaultClient = AI({ httpUrl } as AIClientOptions) as unknown as AIClient
|
|
706
|
+
} else {
|
|
707
|
+
throw new Error(
|
|
708
|
+
'AI client not configured. Call configureAI() first or set AI_WS_URL/AI_HTTP_URL environment variables.'
|
|
709
|
+
)
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return defaultClient
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Base class for implementing AI services
|
|
717
|
+
*
|
|
718
|
+
* Extend this class to create your own AI service implementation.
|
|
719
|
+
*
|
|
720
|
+
* @example
|
|
721
|
+
* ```ts
|
|
722
|
+
* class MyAIService extends AIServiceTarget {
|
|
723
|
+
* async generate(options: AIGenerateOptions): Promise<AIGenerateResult> {
|
|
724
|
+
* // Your implementation
|
|
725
|
+
* }
|
|
726
|
+
* }
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
export abstract class AIServiceTarget extends RpcTarget implements AIClient {
|
|
730
|
+
abstract generate(options: AIGenerateOptions): RpcPromise<AIGenerateResult>
|
|
731
|
+
abstract do(action: string, context?: unknown): RpcPromise<unknown>
|
|
732
|
+
abstract is(value: unknown, type: string | JSONSchema): RpcPromise<boolean>
|
|
733
|
+
abstract code(prompt: string, language?: string): RpcPromise<string>
|
|
734
|
+
abstract decide<T extends string>(options: T[], context?: string): RpcPromise<T>
|
|
735
|
+
abstract diagram(description: string, format?: 'mermaid' | 'svg' | 'ascii'): RpcPromise<string>
|
|
736
|
+
abstract image(prompt: string, options?: ImageOptions): RpcPromise<ImageResult>
|
|
737
|
+
abstract video(prompt: string, options?: VideoOptions): RpcPromise<VideoResult>
|
|
738
|
+
abstract write(prompt: string, options?: WriteOptions): RpcPromise<string>
|
|
739
|
+
abstract list(prompt: string): RpcPromise<ListResult>
|
|
740
|
+
abstract lists(prompt: string): RpcPromise<ListsResult>
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Standalone function for defining AI functions
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```ts
|
|
748
|
+
* import { defineFunction } from 'ai-functions'
|
|
749
|
+
*
|
|
750
|
+
* const summarize = defineFunction({
|
|
751
|
+
* type: 'generative',
|
|
752
|
+
* name: 'summarize',
|
|
753
|
+
* args: { text: 'Text to summarize' },
|
|
754
|
+
* output: 'string',
|
|
755
|
+
* promptTemplate: 'Summarize: {{text}}',
|
|
756
|
+
* })
|
|
757
|
+
*
|
|
758
|
+
* const result = await summarize.call({ text: 'Long article...' })
|
|
759
|
+
* ```
|
|
760
|
+
*/
|
|
761
|
+
export function defineFunction<TArgs, TReturn>(
|
|
762
|
+
definition: FunctionDefinition<TArgs, TReturn>
|
|
763
|
+
): DefinedFunction<TArgs, TReturn> {
|
|
764
|
+
return createDefinedFunction(definition)
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// ============================================================================
|
|
768
|
+
// Function Registry
|
|
769
|
+
// ============================================================================
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* In-memory function registry
|
|
773
|
+
*/
|
|
774
|
+
class InMemoryFunctionRegistry implements FunctionRegistry {
|
|
775
|
+
private functions = new Map<string, DefinedFunction>()
|
|
776
|
+
|
|
777
|
+
get(name: string): DefinedFunction | undefined {
|
|
778
|
+
return this.functions.get(name)
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
set(name: string, fn: DefinedFunction): void {
|
|
782
|
+
this.functions.set(name, fn)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
has(name: string): boolean {
|
|
786
|
+
return this.functions.has(name)
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
list(): string[] {
|
|
790
|
+
return Array.from(this.functions.keys())
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
delete(name: string): boolean {
|
|
794
|
+
return this.functions.delete(name)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
clear(): void {
|
|
798
|
+
this.functions.clear()
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* Global function registry
|
|
804
|
+
*
|
|
805
|
+
* Note: This is in-memory only. For persistence, use mdxai or mdxdb packages.
|
|
806
|
+
*/
|
|
807
|
+
export const functions: FunctionRegistry = new InMemoryFunctionRegistry()
|
|
808
|
+
|
|
809
|
+
// ============================================================================
|
|
810
|
+
// Auto-Define Functions
|
|
811
|
+
// ============================================================================
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Analyze a function call and determine what type of function it should be
|
|
815
|
+
*/
|
|
816
|
+
async function analyzeFunction(
|
|
817
|
+
name: string,
|
|
818
|
+
args: Record<string, unknown>
|
|
819
|
+
): Promise<AutoDefineResult> {
|
|
820
|
+
// Convert camelCase/snake_case to readable name
|
|
821
|
+
const readableName = name
|
|
822
|
+
.replace(/([A-Z])/g, ' $1')
|
|
823
|
+
.replace(/_/g, ' ')
|
|
824
|
+
.toLowerCase()
|
|
825
|
+
.trim()
|
|
826
|
+
|
|
827
|
+
const argDescriptions = Object.entries(args)
|
|
828
|
+
.map(([key, value]) => {
|
|
829
|
+
const type = Array.isArray(value) ? 'array' : typeof value
|
|
830
|
+
return ` - ${key}: ${type} (example: ${JSON.stringify(value).slice(0, 50)})`
|
|
831
|
+
})
|
|
832
|
+
.join('\n')
|
|
833
|
+
|
|
834
|
+
const result = await generateObject({
|
|
835
|
+
model: 'sonnet',
|
|
836
|
+
schema: {
|
|
837
|
+
type: 'code | generative | agentic | human',
|
|
838
|
+
reasoning: 'Why this function type is appropriate (1-2 sentences)',
|
|
839
|
+
description: 'What this function does',
|
|
840
|
+
output: 'string | object | image | video | audio',
|
|
841
|
+
returnType: 'Schema for the return type as a SimpleSchema object',
|
|
842
|
+
system: 'System prompt for the AI (if generative/agentic)',
|
|
843
|
+
promptTemplate: 'Prompt template with {{arg}} placeholders',
|
|
844
|
+
instructions: 'Instructions for agentic/human functions',
|
|
845
|
+
needsTools: 'true | false',
|
|
846
|
+
suggestedTools: ['Names of tools that might be needed'],
|
|
847
|
+
channel: 'slack | email | web | sms | custom',
|
|
848
|
+
},
|
|
849
|
+
system: `You are an expert at designing AI functions. Analyze the function name and arguments to determine the best function type.
|
|
850
|
+
|
|
851
|
+
Function Types:
|
|
852
|
+
- "code": For generating executable code (calculations, algorithms, data transformations)
|
|
853
|
+
- "generative": For generating content (text, summaries, translations, creative writing, structured data)
|
|
854
|
+
- "agentic": For complex tasks requiring multiple steps, research, or tool use (research, planning, multi-step workflows)
|
|
855
|
+
- "human": For tasks requiring human judgment, approval, or input (approvals, reviews, decisions)
|
|
856
|
+
|
|
857
|
+
Guidelines:
|
|
858
|
+
- Most functions should be "generative" - they generate content or structured data
|
|
859
|
+
- Use "code" only when actual executable code needs to be generated
|
|
860
|
+
- Use "agentic" when the task requires research, multiple steps, or external tool use
|
|
861
|
+
- Use "human" when human judgment/approval is essential`,
|
|
862
|
+
prompt: `Analyze this function call and determine how to define it:
|
|
863
|
+
|
|
864
|
+
Function Name: ${name}
|
|
865
|
+
Readable Name: ${readableName}
|
|
866
|
+
Arguments:
|
|
867
|
+
${argDescriptions || ' (no arguments)'}
|
|
868
|
+
|
|
869
|
+
Determine:
|
|
870
|
+
1. What type of function this should be
|
|
871
|
+
2. What it should return
|
|
872
|
+
3. How it should be implemented`,
|
|
873
|
+
})
|
|
874
|
+
|
|
875
|
+
const analysis = result.object as {
|
|
876
|
+
type: string
|
|
877
|
+
reasoning: string
|
|
878
|
+
description: string
|
|
879
|
+
output: string
|
|
880
|
+
returnType: unknown
|
|
881
|
+
system: string
|
|
882
|
+
promptTemplate: string
|
|
883
|
+
instructions: string
|
|
884
|
+
needsTools: string
|
|
885
|
+
suggestedTools: string[]
|
|
886
|
+
channel: string
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Build the function definition based on the analysis
|
|
890
|
+
let definition: FunctionDefinition
|
|
891
|
+
|
|
892
|
+
const baseDefinition = {
|
|
893
|
+
name,
|
|
894
|
+
description: analysis.description,
|
|
895
|
+
args: inferArgsSchema(args),
|
|
896
|
+
returnType: analysis.returnType as SimpleSchemaType,
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
switch (analysis.type) {
|
|
900
|
+
case 'code':
|
|
901
|
+
definition = {
|
|
902
|
+
...baseDefinition,
|
|
903
|
+
type: 'code' as const,
|
|
904
|
+
language: 'typescript' as const,
|
|
905
|
+
instructions: analysis.instructions,
|
|
906
|
+
}
|
|
907
|
+
break
|
|
908
|
+
|
|
909
|
+
case 'agentic':
|
|
910
|
+
definition = {
|
|
911
|
+
...baseDefinition,
|
|
912
|
+
type: 'agentic' as const,
|
|
913
|
+
instructions: analysis.instructions || `Complete the ${readableName} task`,
|
|
914
|
+
promptTemplate: analysis.promptTemplate,
|
|
915
|
+
tools: [], // Tools would need to be provided separately
|
|
916
|
+
maxIterations: 10,
|
|
917
|
+
}
|
|
918
|
+
break
|
|
919
|
+
|
|
920
|
+
case 'human':
|
|
921
|
+
definition = {
|
|
922
|
+
...baseDefinition,
|
|
923
|
+
type: 'human' as const,
|
|
924
|
+
channel: (analysis.channel || 'web') as HumanChannel,
|
|
925
|
+
instructions: analysis.instructions || `Please review and respond to this ${readableName} request`,
|
|
926
|
+
promptTemplate: analysis.promptTemplate,
|
|
927
|
+
}
|
|
928
|
+
break
|
|
929
|
+
|
|
930
|
+
case 'generative':
|
|
931
|
+
default:
|
|
932
|
+
definition = {
|
|
933
|
+
...baseDefinition,
|
|
934
|
+
type: 'generative' as const,
|
|
935
|
+
output: (analysis.output || 'object') as 'string' | 'object' | 'image' | 'video' | 'audio',
|
|
936
|
+
system: analysis.system,
|
|
937
|
+
promptTemplate: analysis.promptTemplate || `{{${Object.keys(args)[0] || 'input'}}}`,
|
|
938
|
+
}
|
|
939
|
+
break
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return {
|
|
943
|
+
type: analysis.type as 'code' | 'generative' | 'agentic' | 'human',
|
|
944
|
+
reasoning: analysis.reasoning,
|
|
945
|
+
definition,
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/**
|
|
950
|
+
* Infer a schema from example arguments
|
|
951
|
+
*/
|
|
952
|
+
function inferArgsSchema(args: Record<string, unknown>): Record<string, string | string[] | Record<string, unknown>> {
|
|
953
|
+
const schema: Record<string, string | string[] | Record<string, unknown>> = {}
|
|
954
|
+
|
|
955
|
+
for (const [key, value] of Object.entries(args)) {
|
|
956
|
+
if (typeof value === 'string') {
|
|
957
|
+
schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`
|
|
958
|
+
} else if (typeof value === 'number') {
|
|
959
|
+
schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()} (number)`
|
|
960
|
+
} else if (typeof value === 'boolean') {
|
|
961
|
+
schema[key] = `Whether ${key.replace(/([A-Z])/g, ' $1').toLowerCase()} (boolean)`
|
|
962
|
+
} else if (Array.isArray(value)) {
|
|
963
|
+
if (value.length > 0 && typeof value[0] === 'string') {
|
|
964
|
+
schema[key] = [`List of ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`]
|
|
965
|
+
} else {
|
|
966
|
+
schema[key] = [`Items for ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`]
|
|
967
|
+
}
|
|
968
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
969
|
+
schema[key] = inferArgsSchema(value as Record<string, unknown>)
|
|
970
|
+
} else {
|
|
971
|
+
schema[key] = `The ${key.replace(/([A-Z])/g, ' $1').toLowerCase()}`
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
return schema
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Auto-define a function based on its name and arguments, or define with explicit definition
|
|
980
|
+
*
|
|
981
|
+
* When called with (name, args), uses AI to analyze and determine:
|
|
982
|
+
* - What type of function it should be (code, generative, agentic, human)
|
|
983
|
+
* - What it should return
|
|
984
|
+
* - How it should be implemented
|
|
985
|
+
*
|
|
986
|
+
* When called with a FunctionDefinition, creates the function directly.
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* ```ts
|
|
990
|
+
* // Auto-define from name and example args
|
|
991
|
+
* const planTrip = await define('planTrip', { destination: 'Tokyo', travelers: 2 })
|
|
992
|
+
*
|
|
993
|
+
* // Or define explicitly
|
|
994
|
+
* const summarize = define.generative({
|
|
995
|
+
* name: 'summarize',
|
|
996
|
+
* args: { text: 'Text to summarize' },
|
|
997
|
+
* output: 'string',
|
|
998
|
+
* })
|
|
999
|
+
*
|
|
1000
|
+
* // Or with full definition
|
|
1001
|
+
* const fn = defineFunction({
|
|
1002
|
+
* type: 'generative',
|
|
1003
|
+
* name: 'translate',
|
|
1004
|
+
* args: { text: 'Text', lang: 'Target language' },
|
|
1005
|
+
* output: 'string',
|
|
1006
|
+
* })
|
|
1007
|
+
* ```
|
|
1008
|
+
*/
|
|
1009
|
+
async function autoDefineImpl(
|
|
1010
|
+
name: string,
|
|
1011
|
+
args: Record<string, unknown>
|
|
1012
|
+
): Promise<DefinedFunction> {
|
|
1013
|
+
// Check if already defined
|
|
1014
|
+
const existing = functions.get(name)
|
|
1015
|
+
if (existing) {
|
|
1016
|
+
return existing
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// Analyze and define the function
|
|
1020
|
+
const { definition } = await analyzeFunction(name, args)
|
|
1021
|
+
|
|
1022
|
+
// Create the defined function
|
|
1023
|
+
const definedFn = createDefinedFunction(definition)
|
|
1024
|
+
|
|
1025
|
+
// Store in registry
|
|
1026
|
+
functions.set(name, definedFn)
|
|
1027
|
+
|
|
1028
|
+
return definedFn
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* Define functions - auto-define or use typed helpers
|
|
1033
|
+
*/
|
|
1034
|
+
export const define = Object.assign(autoDefineImpl, {
|
|
1035
|
+
/**
|
|
1036
|
+
* Define a code generation function
|
|
1037
|
+
*/
|
|
1038
|
+
code: <TArgs, TReturn>(
|
|
1039
|
+
definition: Omit<CodeFunctionDefinition<TArgs, TReturn>, 'type'>
|
|
1040
|
+
): DefinedFunction<TArgs, TReturn> => {
|
|
1041
|
+
const fn = defineFunction({ type: 'code', ...definition } as CodeFunctionDefinition<TArgs, TReturn>)
|
|
1042
|
+
functions.set(definition.name, fn as DefinedFunction)
|
|
1043
|
+
return fn
|
|
1044
|
+
},
|
|
1045
|
+
|
|
1046
|
+
/**
|
|
1047
|
+
* Define a generative function
|
|
1048
|
+
*/
|
|
1049
|
+
generative: <TArgs, TReturn>(
|
|
1050
|
+
definition: Omit<GenerativeFunctionDefinition<TArgs, TReturn>, 'type'>
|
|
1051
|
+
): DefinedFunction<TArgs, TReturn> => {
|
|
1052
|
+
const fn = defineFunction({ type: 'generative', ...definition } as GenerativeFunctionDefinition<TArgs, TReturn>)
|
|
1053
|
+
functions.set(definition.name, fn as DefinedFunction)
|
|
1054
|
+
return fn
|
|
1055
|
+
},
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Define an agentic function
|
|
1059
|
+
*/
|
|
1060
|
+
agentic: <TArgs, TReturn>(
|
|
1061
|
+
definition: Omit<AgenticFunctionDefinition<TArgs, TReturn>, 'type'>
|
|
1062
|
+
): DefinedFunction<TArgs, TReturn> => {
|
|
1063
|
+
const fn = defineFunction({ type: 'agentic', ...definition } as AgenticFunctionDefinition<TArgs, TReturn>)
|
|
1064
|
+
functions.set(definition.name, fn as DefinedFunction)
|
|
1065
|
+
return fn
|
|
1066
|
+
},
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Define a human-in-the-loop function
|
|
1070
|
+
*/
|
|
1071
|
+
human: <TArgs, TReturn>(
|
|
1072
|
+
definition: Omit<HumanFunctionDefinition<TArgs, TReturn>, 'type'>
|
|
1073
|
+
): DefinedFunction<TArgs, TReturn> => {
|
|
1074
|
+
const fn = defineFunction({ type: 'human', ...definition } as HumanFunctionDefinition<TArgs, TReturn>)
|
|
1075
|
+
functions.set(definition.name, fn as DefinedFunction)
|
|
1076
|
+
return fn
|
|
1077
|
+
},
|
|
1078
|
+
})
|
|
1079
|
+
|
|
1080
|
+
// ============================================================================
|
|
1081
|
+
// AI() - Smart AI Client with Auto-Definition
|
|
1082
|
+
// ============================================================================
|
|
1083
|
+
|
|
1084
|
+
/** Known built-in method names that should not be auto-defined */
|
|
1085
|
+
const BUILTIN_METHODS = new Set([
|
|
1086
|
+
'do', 'is', 'code', 'decide', 'diagram', 'generate', 'image', 'video', 'write', 'list', 'lists',
|
|
1087
|
+
'functions', 'define', 'defineFunction', 'then', 'catch', 'finally',
|
|
1088
|
+
])
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Create a smart AI client that auto-defines functions on first call
|
|
1092
|
+
*
|
|
1093
|
+
* @example
|
|
1094
|
+
* ```ts
|
|
1095
|
+
* const ai = AI()
|
|
1096
|
+
*
|
|
1097
|
+
* // First call - auto-defines the function
|
|
1098
|
+
* const trip = await ai.planTrip({
|
|
1099
|
+
* destination: 'Tokyo',
|
|
1100
|
+
* dates: { start: '2024-03-01', end: '2024-03-10' },
|
|
1101
|
+
* travelers: 2,
|
|
1102
|
+
* })
|
|
1103
|
+
*
|
|
1104
|
+
* // Second call - uses cached definition (in-memory)
|
|
1105
|
+
* const trip2 = await ai.planTrip({
|
|
1106
|
+
* destination: 'Paris',
|
|
1107
|
+
* dates: { start: '2024-06-01', end: '2024-06-07' },
|
|
1108
|
+
* travelers: 4,
|
|
1109
|
+
* })
|
|
1110
|
+
*
|
|
1111
|
+
* // Access registry and define
|
|
1112
|
+
* console.log(ai.functions.list()) // ['planTrip']
|
|
1113
|
+
* ai.define.generative({ name: 'summarize', ... })
|
|
1114
|
+
* ```
|
|
1115
|
+
*/
|
|
1116
|
+
function createSmartAI(): AIProxy {
|
|
1117
|
+
const base = {
|
|
1118
|
+
functions,
|
|
1119
|
+
define,
|
|
1120
|
+
defineFunction,
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
return new Proxy(base as AIProxy, {
|
|
1124
|
+
get(target, prop: string) {
|
|
1125
|
+
// Return built-in properties
|
|
1126
|
+
if (prop in target) {
|
|
1127
|
+
return (target as Record<string, unknown>)[prop]
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Skip internal properties
|
|
1131
|
+
if (typeof prop === 'symbol' || prop.startsWith('_') || BUILTIN_METHODS.has(prop)) {
|
|
1132
|
+
return undefined
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// Return a function that auto-defines and calls
|
|
1136
|
+
return async (args: Record<string, unknown> = {}) => {
|
|
1137
|
+
// Check if function is already defined
|
|
1138
|
+
let fn = functions.get(prop)
|
|
1139
|
+
|
|
1140
|
+
if (!fn) {
|
|
1141
|
+
// Auto-define the function
|
|
1142
|
+
fn = await define(prop, args)
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// Call the function
|
|
1146
|
+
return fn.call(args)
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
})
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Type for the AI proxy with auto-define capability
|
|
1154
|
+
*/
|
|
1155
|
+
export interface AIProxy {
|
|
1156
|
+
/** Function registry */
|
|
1157
|
+
functions: FunctionRegistry
|
|
1158
|
+
/** Define functions */
|
|
1159
|
+
define: typeof define
|
|
1160
|
+
/** Define a function with full definition */
|
|
1161
|
+
defineFunction: typeof defineFunction
|
|
1162
|
+
/** Dynamic function calls */
|
|
1163
|
+
[key: string]: unknown
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* Default AI instance with auto-define capability
|
|
1168
|
+
*
|
|
1169
|
+
* @example
|
|
1170
|
+
* ```ts
|
|
1171
|
+
* import { ai } from 'ai-functions'
|
|
1172
|
+
*
|
|
1173
|
+
* // Auto-define and call
|
|
1174
|
+
* const result = await ai.summarize({ text: 'Long article...' })
|
|
1175
|
+
*
|
|
1176
|
+
* // Access functions registry
|
|
1177
|
+
* ai.functions.list()
|
|
1178
|
+
*
|
|
1179
|
+
* // Define explicitly
|
|
1180
|
+
* ai.define.generative({ name: 'translate', ... })
|
|
1181
|
+
* ```
|
|
1182
|
+
*/
|
|
1183
|
+
export const ai: AIProxy = createSmartAI()
|