diff --git a/examples/llm-with-search.ts b/examples/llm-with-search.ts index fba339b..6edf471 100644 --- a/examples/llm-with-search.ts +++ b/examples/llm-with-search.ts @@ -39,6 +39,7 @@ async function main() { .call({ searchResults }) + console.log(res) } main() diff --git a/package.json b/package.json index 24bf328..c97a792 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "js-tiktoken": "^1.0.6", "jsonrepair": "^3.1.0", "ky": "^0.33.3", + "nanoid": "^4.0.2", "openai-fetch": "^1.5.1", "p-map": "^6.0.0", "p-retry": "^5.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eeb0692..0e3316f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ dependencies: ky: specifier: ^0.33.3 version: 0.33.3 + nanoid: + specifier: ^4.0.2 + version: 4.0.2 openai-fetch: specifier: ^1.5.1 version: 1.5.1 @@ -2807,6 +2810,12 @@ packages: thenify-all: 1.6.0 dev: true + /nanoid@4.0.2: + resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} + engines: {node: ^14 || ^16 || >=18} + hasBin: true + dev: false + /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true diff --git a/src/agentic.ts b/src/agentic.ts index 45e2baa..5fd39bd 100644 --- a/src/agentic.ts +++ b/src/agentic.ts @@ -6,6 +6,7 @@ import { HumanFeedbackMechanism, HumanFeedbackMechanismCLI } from './human-feedback' +import { defaultIDGeneratorFn } from './utils' export class Agentic { // _taskMap: WeakMap> @@ -20,6 +21,8 @@ export class Agentic { > protected _defaultHumanFeedbackMechamism?: HumanFeedbackMechanism + protected _idGeneratorFn: types.IDGeneratorFunction + constructor(opts: { openai?: types.openai.OpenAIClient anthropic?: types.anthropic.Client @@ -29,6 +32,7 @@ export class Agentic { 'provider' | 'model' | 'modelParams' | 'timeoutMs' | 'retryConfig' > defaultHumanFeedbackMechanism?: HumanFeedbackMechanism + idGeneratorFn?: types.IDGeneratorFunction }) { this._openai = opts.openai this._anthropic = opts.anthropic @@ -54,6 +58,8 @@ export class Agentic { this._defaultHumanFeedbackMechamism = opts.defaultHumanFeedbackMechanism ?? new HumanFeedbackMechanismCLI({ agentic: this }) + + this._idGeneratorFn = opts.idGeneratorFn ?? defaultIDGeneratorFn } public get openai(): types.openai.OpenAIClient | undefined { @@ -68,6 +74,10 @@ export class Agentic { return this._defaultHumanFeedbackMechamism } + public get idGeneratorFn(): types.IDGeneratorFunction { + return this._idGeneratorFn + } + llm( promptOrChatCompletionParams: | string diff --git a/src/llms/llm.ts b/src/llms/llm.ts index 8d586ce..4806448 100644 --- a/src/llms/llm.ts +++ b/src/llms/llm.ts @@ -83,6 +83,10 @@ export abstract class BaseLLM< } } + public override get name(): string { + return `${this._provider}:chat:${this._model}` + } + examples(examples: types.LLMExample[]) { this._examples = examples return this diff --git a/src/task.ts b/src/task.ts index f32d9a1..7c77d9f 100644 --- a/src/task.ts +++ b/src/task.ts @@ -6,9 +6,9 @@ import * as types from '@/types' import { Agentic } from '@/agentic' /** - * A `Task` is a typed, async function call that may be non-deterministic. - * - * Invoking a task is equivalent to sampling from a probability distribution. + * A `Task` is an async function call that may be non-deterministic. It has + * structured input and structured output. Invoking a task is equivalent to + * sampling from a probability distribution. * * Examples of tasks include: * - LLM calls @@ -23,6 +23,7 @@ export abstract class BaseTask< TOutput extends ZodRawShape | ZodTypeAny = ZodTypeAny > { protected _agentic: Agentic + protected _id: string protected _timeoutMs?: number protected _retryConfig: types.RetryConfig @@ -34,21 +35,21 @@ export abstract class BaseTask< retries: 3, strategy: 'default' } + this._id = options.id ?? this._agentic.idGeneratorFn() } public get agentic(): Agentic { return this._agentic } + public get id(): string { + return this._id + } + public abstract get inputSchema(): TInput public abstract get outputSchema(): TOutput - // TODO - // public abstract get nameForModel(): string - // public abstract get nameForHuman(): string - - // public abstract get descForModel(): string - // public abstract get descForHuman(): string + public abstract get name(): string public retryConfig(retryConfig: types.RetryConfig) { this._retryConfig = retryConfig @@ -83,7 +84,10 @@ export abstract class BaseTask< const ctx: types.TaskCallContext = { input, attemptNumber: 0, - metadata: {} + metadata: { + taskName: this.name, + taskId: this.id + } } const result = await pRetry(() => this._call(ctx), { @@ -93,6 +97,7 @@ export abstract class BaseTask< await Promise.resolve(this._retryConfig.onFailedAttempt(err)) } + // TODO: log this task error ctx.attemptNumber = err.attemptNumber + 1 ctx.metadata.error = err diff --git a/src/tools/metaphor.ts b/src/tools/metaphor.ts index 2c59f08..c581bd1 100644 --- a/src/tools/metaphor.ts +++ b/src/tools/metaphor.ts @@ -58,6 +58,10 @@ export class MetaphorSearchTool extends BaseTask< return MetaphorSearchToolOutputSchema } + public override get name(): string { + return 'metaphor-search' + } + protected override async _call( ctx: types.TaskCallContext ): Promise { diff --git a/src/tools/novu.ts b/src/tools/novu.ts index 1af1258..40f639a 100644 --- a/src/tools/novu.ts +++ b/src/tools/novu.ts @@ -63,6 +63,10 @@ export class NovuNotificationTool extends BaseTask< return NovuNotificationToolOutputSchema } + public override get name(): string { + return 'novu' + } + protected override async _call( ctx: types.TaskCallContext ): Promise { diff --git a/src/types.ts b/src/types.ts index db1efe8..4c8ac48 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,6 +34,7 @@ export interface BaseTaskOptions { timeoutMs?: number retryConfig?: RetryConfig + id?: string // TODO // caching config @@ -115,8 +116,8 @@ export type TaskError = export interface TaskResponseMetadata extends Record { // task info - // - task name - // - task id + taskName: string + taskId: string // execution info success?: boolean @@ -146,7 +147,9 @@ export interface TaskCallContext< retryMessage?: string attemptNumber: number - metadata: Partial + metadata: TMetadata } +export type IDGeneratorFunction = () => string + // export type ProgressFunction = (partialResponse: ChatMessage) => void diff --git a/src/utils.ts b/src/utils.ts index 447a7bc..1863a35 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,7 @@ +import { customAlphabet, urlAlphabet } from 'nanoid' + +import * as types from './types' + export const extractJSONObjectFromString = (text: string): string | undefined => text.match(/\{(.|\n)*\}/gm)?.[0] @@ -6,3 +10,6 @@ export const extractJSONArrayFromString = (text: string): string | undefined => export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) + +export const defaultIDGeneratorFn: types.IDGeneratorFunction = + customAlphabet(urlAlphabet)