-
Notifications
You must be signed in to change notification settings - Fork 2.7k
wip: v1 chat model #8742
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v1
Are you sure you want to change the base?
wip: v1 chat model #8742
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
dc230b5
to
8463f1b
Compare
* and the run ID. | ||
*/ | ||
handleChatModelStart?( | ||
llm: Serialized, | ||
messages: BaseMessage[][], | ||
messages: Message[], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the inner workings of BaseLanguageModel
lets you parse multiple prompts at once, whereas the new chat model class only takes one string of messages
| SimpleChatModel<Runnable<Message, Output>> | ||
| SimpleChatModel<Runnable<Message, { raw: Message; parsed: Output }>> { | ||
return super.withStructuredOutput(schema, config) as | ||
| SimpleChatModel<Runnable<Message, Output>> | ||
| SimpleChatModel<Runnable<Message, { raw: Message; parsed: Output }>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately I'm not sure if there's a clean solution to avoid this verbose typing
.bindTools([makeToolDefinition("tool1")]) | ||
.withStructuredOutput({ | ||
type: "object", | ||
properties: {}, | ||
required: [], | ||
}) | ||
.bindTools([makeToolDefinition("tool2")]) | ||
.withConfig({ | ||
tool_choice: "any", | ||
}) | ||
.withStructuredOutput({ | ||
type: "object", | ||
properties: {}, | ||
required: [], | ||
}) | ||
.bindTools([makeToolDefinition("tool3")]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would expect that this composition means:
- we keep tools 1-3
tool_choice
will be any- we only use the second withStructuredOutput
constructor(protected params: BaseChatModelParams) { | ||
super(params); | ||
this.verbose = params.verbose ?? false; | ||
this.callbacks = params.callbacks; | ||
this.tags = params.tags ?? []; | ||
this.metadata = params.metadata ?? {}; | ||
this.cache = iife(() => { | ||
if (typeof params.cache === "object") { | ||
return params.cache; | ||
} else if (params.cache) { | ||
return InMemoryCacheV1.global(); | ||
} | ||
return undefined; | ||
}); | ||
this.caller = new AsyncCaller(params ?? {}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this constructor is largely just taking after BaseLangChain
|
||
export abstract class BaseChatModelV1< | ||
CallOptions extends BaseChatModelCallOptions = BaseChatModelCallOptions, | ||
TOutputParser extends ChatModelOutputParser | undefined = undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TOutputParser actually determines whats on the output end of the runnable -- if we want structured output out we use whats defined on the output of the passed in parser, if its undefined we use Message
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has a naming conflict with non-chat model output parsers, so am open to ideas on what to name this if we stick w/ this
if (this.outputParser === undefined) { | ||
yield chunk as InferChatModelOutput<TOutputParser>; | ||
} else { | ||
yield this.outputParser.invoke( | ||
chunk, | ||
callOptions | ||
) as InferChatModelOutput<TOutputParser>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unclear on what the semantics should be for parsing chunks
- run the output parser on each chunk (what this is currently doing)
- only run the output parser on the aggregated chunk
return instance.withOutputParser( | ||
toolMessageParser.withConfig({ | ||
runName: "StructuredOutput", | ||
}) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of returning a runnable, we return a reconstructed instance that has a different outputParser
98250ec
to
1c42611
Compare
some highlights from this iteration of the
BaseChatModel
class:BaseChatModel
now inherits fromRunnable
instead ofBaseLanguageModel
Message
objects as the output of the "chat model runnable" instead of the legacyGeneration
objecthandleChatModelX
instead ofhandleLLMX
)Message
instead ofGeneration
objects in eventsBaseCache
that usesMessage
objects instead ofGeneration
objectsMessage
's to later enable replaying stream chunksRunnableBinding
when callingwithStructuredOutput
,bindTools
, orwithConfig
, we instead create a new instance of the current class by callingthis.constructor
and modifying an internaldefaultOptions
property that gets reusedwithStructuredOutput
specifically, we're retaining runnable semantics by pinning the existing runnable chain to a.outputParser
property that gets used ininvoke
and_streamIterator
withStructuredOutput
andbindTools
together_generate
->generate
generate
->invoke
_streamResponseChunks
->streamResponseChunks
BaseLanguageModel
very wip, I don't expect CI to pass