Skip to content
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

refactor!: Simplify LLMResult and ChatResult classes #363

Merged
merged 5 commits into from
Apr 6, 2024

Conversation

davidmigloz
Copy link
Owner

@davidmigloz davidmigloz commented Apr 6, 2024

Relates to #266

We've updated the LanguageModelResult class structure, along with its subclasses LLMResult and ChatResult, to simplify their design. Previously, these classes contained a list of generations for each model's outputs, complicating single-output access since users often require only one generation.

Key Changes:

  1. Simplified design: each LanguageModelResult now stores a single output directly. To generate multiple outputs, use the new .batch method (instead of .invoke or .stream), which returns a list of LanguageModelResult instances.

  2. Unified finish reason: we've standardized the finish reason across all providers, simplifying the process of switching between providers for users who have logic dependent on the finish reason.

Result class details:

LLMs like OpenAI returns an LLMResult, chat models like ChatOpenAI returns a ChatResult. Both classes inherit from LanguageModelResult and contain:

  • id: Identifier for the generation.
  • output: The text output (a String for LLMs) or an AIChatMessage for chat models.
  • finishReason: Enum specifying why the model ceased token generation.
  • metadata: A map containing provider-specific details about the generation (such as model info, block reason, citations, etc.).
  • usage: Token usage statistics, provided by the model.
  • streaming: Indicates if the result is streamed.

Migration guide

LLM output

To get the String output from an LLM (e.g. OpenAI):

final res = await llm.invoke(promptValue);

Before: The following options were available to get the String output

final output1 = res.generations.first.output;
final output2 = res.firstOutput;
final output3 = res.firstOutputAsString;

After:

final output1 = res.output; // The output is already a String
final output2 = res.outputAsString; // You can also use `outputAsString` which is available in both LLMs and ChatModels

Chat model output

To get the String output from a Chat Model (e.g. ChatOpenAI):

final res = await chatModel.invoke(promptValue);

Before: The following options were available to get the String output

final output1 = res.generations.first.output.content;
final output2 = res.firstOutput.content;
final output3 = res.firstOutputAsString;

After:

final output1 = res.output.content; // The output of a Chat Models is an AIChatMessage, so we need to get the `content`
final output2 = res.outputAsString; // You can also use `outputAsString` which is available in both LLMs and ChatModels

StringOutputParser

If you are using a StringOutputParser in a chain to get the output as String you don't have to change anything.

final chain = promptTemplate.pipe(llm).pipe(StringOutputParser());
final outptut = await chain.invoke({'foo': 'bar'});

Finish reason

To get the finish reason of a generation:

Before: It was part of the generationInfo metadata, and it was of type String with different values for different providers

final finishReason = res.generations.first.generationInfo?['finish_reason'];

After: It is now an enum with standardized values for all the providers. If a providers doesn't return a finish reason, FinishReason.unspecified is returned.

final finishReason = res.finishReason;

Unified finish reasons:

enum FinishReason {
  /// The model hit a natural stop point or a provided stop sequence.
  stop,
  /// The maximum number of tokens specified in the request was reached.
  length,
  /// The content was flagged for content filter reasons.
  contentFilter,
  /// The content content was flagged for recitation reasons.
  recitation,
  /// The model called a tool.
  toolCalls,
  /// The finish reason is unspecified.
  unspecified,
}

ID

Before not every provider was returning a generation id, now all of them do.

final id = res.id;

Token usage

You can still access the number of tokens consumed for the generation (not every provider returns it).

final usage = res.usage;

Other metadata

To access other provider-specific metadata:

Before:

final metadata1 = res.modelOutput;
final metadata2 = res.generations.first.generationInfo;

After:

final metadata = res.metadata;

Batch generation

Some providers (e.g. OpenAI) used to allow generating multiple outputs for the same prompt in a single request:

Before:

final chatModel = ChatOpenAI();
final res = await chatModel.invoke(
  PromptValue.string('prompt'),
  options: const ChatOpenAIOptions(
    n: 3,
  ),
);
final output1 = res.generations[0].output.content;
final output2 = res.generations[1].output.content;
final output3 = res.generations[2].output.content;

The same functionality can now be achieved with the new standard .bach method that every Runnable supports.

After:

TODO

@davidmigloz davidmigloz self-assigned this Apr 6, 2024
@davidmigloz davidmigloz mentioned this pull request Apr 6, 2024
16 tasks
@davidmigloz davidmigloz added c:llms Models and integrations. c:chat-models Chat models. c:lcel LangChain Expression Language t:enhancement New feature or request labels Apr 6, 2024
@davidmigloz davidmigloz added this to the v0.5.0 milestone Apr 6, 2024
@davidmigloz davidmigloz merged commit ffe539c into main Apr 6, 2024
1 check passed
@davidmigloz davidmigloz deleted the invoke-refactor branch April 6, 2024 17:12
KennethKnudsen97 pushed a commit to KennethKnudsen97/langchain_dart that referenced this pull request Apr 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c:chat-models Chat models. c:lcel LangChain Expression Language c:llms Models and integrations. t:enhancement New feature or request
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

1 participant