diff --git a/legacy/src/llms/llm.ts b/legacy/src/llms/llm.ts index 7eca5f0c..945aefe0 100644 --- a/legacy/src/llms/llm.ts +++ b/legacy/src/llms/llm.ts @@ -19,6 +19,7 @@ import { extractJSONObjectFromString } from '@/utils' +// TODO: TInput should only be allowed to be an object export abstract class BaseLLM< TInput = void, TOutput = string, @@ -142,6 +143,28 @@ export abstract class BaseChatModel< this._messages = options.messages } + // TODO: use polymorphic `this` type to return correct BaseLLM subclass type + input(inputSchema: ZodType): BaseChatModel { + const refinedInstance = this as unknown as BaseChatModel< + U, + TOutput, + TModelParams + > + refinedInstance._inputSchema = inputSchema + return refinedInstance + } + + // TODO: use polymorphic `this` type to return correct BaseLLM subclass type + output(outputSchema: ZodType): BaseChatModel { + const refinedInstance = this as unknown as BaseChatModel< + TInput, + U, + TModelParams + > + refinedInstance._outputSchema = outputSchema + return refinedInstance + } + protected abstract _createChatCompletion( messages: types.ChatMessage[] ): Promise> diff --git a/legacy/test/openai.test.ts b/legacy/test/openai.test.ts index 9863706c..6314f2a0 100644 --- a/legacy/test/openai.test.ts +++ b/legacy/test/openai.test.ts @@ -4,10 +4,48 @@ import sinon from 'sinon' import { z } from 'zod' import { OutputValidationError, TemplateValidationError } from '@/errors' -import { OpenAIChatModel } from '@/llms/openai' +import { BaseChatModel, OpenAIChatModel } from '@/llms' import { createTestAgenticRuntime } from './_utils' +test('OpenAIChatModel ⇒ types', async (t) => { + const agentic = createTestAgenticRuntime() + const b = agentic.gpt4('test') + t.pass() + + expectTypeOf(b).toMatchTypeOf>() + + expectTypeOf( + b.input( + z.object({ + foo: z.string() + }) + ) + ).toMatchTypeOf< + BaseChatModel< + { + foo: string + }, + string + > + >() + + expectTypeOf( + b.output( + z.object({ + bar: z.string().optional() + }) + ) + ).toMatchTypeOf< + BaseChatModel< + any, + { + bar?: string + } + > + >() +}) + test('OpenAIChatModel ⇒ string output', async (t) => { t.timeout(2 * 60 * 1000) const agentic = createTestAgenticRuntime() @@ -165,7 +203,7 @@ test('OpenAIChatModel ⇒ template variables', async (t) => { } }) -test.only('OpenAIChatModel ⇒ missing template variable', async (t) => { +test('OpenAIChatModel ⇒ missing template variable', async (t) => { t.timeout(2 * 60 * 1000) const agentic = createTestAgenticRuntime() diff --git a/legacy/test/twilio-conversation.test.ts b/legacy/test/twilio-conversation.test.ts index f815b927..4a254fe3 100644 --- a/legacy/test/twilio-conversation.test.ts +++ b/legacy/test/twilio-conversation.test.ts @@ -15,7 +15,7 @@ test('TwilioConversationClient.createConversation', async (t) => { const conversation = await client.createConversation(friendlyName) t.is(conversation.friendly_name, friendlyName) - client.deleteConversation(conversation.sid) + await client.deleteConversation(conversation.sid) }) test('TwilioConversationClient.addParticipant', async (t) => { @@ -90,7 +90,7 @@ test('TwilioConversationClient.sendAndWaitForReply', async (t) => { await t.throwsAsync( async () => { await client.sendAndWaitForReply({ - recipientPhoneNumber: process.env.TWILIO_TEST_PHONE_NUMBER as string, + recipientPhoneNumber: process.env.TWILIO_TEST_PHONE_NUMBER!, text: 'Please confirm by replying with "yes" or "no".', name: 'wait-for-reply-test', validate: (message) => @@ -122,7 +122,7 @@ test('TwilioConversationClient.sendAndWaitForReply.stopSignal', async (t) => { async () => { const controller = new AbortController() const promise = client.sendAndWaitForReply({ - recipientPhoneNumber: process.env.TWILIO_TEST_PHONE_NUMBER as string, + recipientPhoneNumber: process.env.TWILIO_TEST_PHONE_NUMBER!, text: 'Please confirm by replying with "yes" or "no".', name: 'wait-for-reply-test', validate: (message) =>