Files
dify-plugin-sdks/python/dify_plugin/invocations/model/llm_structured_output.py
Yeuoly 1ec2ff6d6c feat[0.4.0]: add LLMStructuredOutput and LLMStructuredOutputInvocation for s… (#166)
* feat: add LLMStructuredOutput and LLMStructuredOutputInvocation for structured output handling

- Introduced LLMStructuredOutput model class for structured output representation.
- Added LLMStructuredOutputInvocation class to handle invocation with structured output.
- Updated ModelInvocations to include LLMStructuredOutputInvocation.
- Modified InvokeType enum to support LLMStructuredOutput type.

* docs: update README.md to include support for LLM structured output in manifest versioning
2025-06-27 15:54:42 +08:00

116 lines
4.5 KiB
Python

from collections.abc import Generator
from typing import Any, Literal, cast, overload
from dify_plugin.core.entities.invocation import InvokeType
from dify_plugin.core.runtime import BackwardsInvocation
from dify_plugin.entities.model.llm import (
LLMModelConfig,
LLMResultChunkWithStructuredOutput,
LLMResultWithStructuredOutput,
LLMUsage,
)
from dify_plugin.entities.model.message import AssistantPromptMessage, PromptMessage, PromptMessageTool
class LLMStructuredOutputInvocation(BackwardsInvocation[LLMResultChunkWithStructuredOutput]):
@overload
def invoke(
self,
model_config: LLMModelConfig | dict,
prompt_messages: list[PromptMessage],
structured_output_schema: dict[str, Any],
tools: list[PromptMessageTool] | None = None,
stop: list[str] | None = None,
stream: Literal[True] = True,
) -> Generator[LLMResultChunkWithStructuredOutput, None, None]: ...
@overload
def invoke(
self,
model_config: LLMModelConfig | dict,
prompt_messages: list[PromptMessage],
structured_output_schema: dict[str, Any],
tools: list[PromptMessageTool] | None = None,
stop: list[str] | None = None,
stream: Literal[False] = False,
) -> LLMResultWithStructuredOutput: ...
@overload
def invoke(
self,
model_config: LLMModelConfig | dict,
prompt_messages: list[PromptMessage],
structured_output_schema: dict[str, Any],
tools: list[PromptMessageTool] | None = None,
stop: list[str] | None = None,
stream: bool = True,
) -> Generator[LLMResultChunkWithStructuredOutput, None, None] | LLMResultWithStructuredOutput: ...
def invoke(
self,
model_config: LLMModelConfig | dict,
prompt_messages: list[PromptMessage],
structured_output_schema: dict[str, Any],
tools: list[PromptMessageTool] | None = None,
stop: list[str] | None = None,
stream: bool = True,
) -> Generator[LLMResultChunkWithStructuredOutput, None, None] | LLMResultWithStructuredOutput:
"""
Invoke llm with structured output
"""
if isinstance(model_config, dict):
model_config = LLMModelConfig(**model_config)
data = {
**model_config.model_dump(),
"prompt_messages": [message.model_dump() for message in prompt_messages],
"structured_output_schema": structured_output_schema,
"tools": [tool.model_dump() for tool in tools] if tools else None,
"stop": stop,
"stream": stream,
}
if stream:
response = self._backwards_invoke(
InvokeType.LLMStructuredOutput,
LLMResultChunkWithStructuredOutput,
data,
)
response = cast(Generator[LLMResultChunkWithStructuredOutput, None, None], response)
return response
result = LLMResultWithStructuredOutput(
model=model_config.model,
message=AssistantPromptMessage(content=""),
usage=LLMUsage.empty_usage(),
structured_output=None,
)
assert isinstance(result.message.content, str)
for llm_result in self._backwards_invoke(
InvokeType.LLMStructuredOutput,
LLMResultChunkWithStructuredOutput,
data,
):
if isinstance(llm_result.delta.message.content, str):
result.message.content += llm_result.delta.message.content
if len(llm_result.delta.message.tool_calls) > 0:
result.message.tool_calls = llm_result.delta.message.tool_calls
if llm_result.delta.usage:
result.usage.prompt_tokens += llm_result.delta.usage.prompt_tokens
result.usage.completion_tokens += llm_result.delta.usage.completion_tokens
result.usage.total_tokens += llm_result.delta.usage.total_tokens
result.usage.completion_price = llm_result.delta.usage.completion_price
result.usage.prompt_price = llm_result.delta.usage.prompt_price
result.usage.total_price = llm_result.delta.usage.total_price
result.usage.currency = llm_result.delta.usage.currency
result.usage.latency = llm_result.delta.usage.latency
# Handle structured output
if llm_result.structured_output:
result.structured_output = llm_result.structured_output
return result