1- from typing import Any , Dict , List
1+ from typing import Any , Dict , List , Optional
22
33from langchain_core .language_models .chat_models import BaseChatModel
44from langchain_core .messages import BaseMessage
55from ldai import LDMessage , log
6- from ldai .providers .model_runner import ModelRunner
7- from ldai .providers .types import LDAIMetrics , ModelResponse , StructuredResponse
6+ from ldai .providers .types import LDAIMetrics , RunnerResult
87
98from ldai_langchain .langchain_helper import (
109 convert_messages_to_langchain ,
1312)
1413
1514
16- class LangChainModelRunner ( ModelRunner ) :
15+ class LangChainModelRunner :
1716 """
18- ModelRunner implementation for LangChain.
17+ Runner implementation for LangChain chat models .
1918
2019 Holds a fully-configured BaseChatModel.
21- Returned by LangChainConnector.create_model(config).
20+ Returned by ``LangChainRunnerFactory.create_model(config)``.
21+
22+ Implements the unified :class:`~ldai.providers.runner.Runner` protocol via
23+ :meth:`run`.
2224 """
2325
2426 def __init__ (self , llm : BaseChatModel ):
@@ -32,13 +34,37 @@ def get_llm(self) -> BaseChatModel:
3234 """
3335 return self ._llm
3436
35- async def invoke_model (self , messages : List [LDMessage ]) -> ModelResponse :
37+ async def run (
38+ self ,
39+ input : Any ,
40+ output_type : Optional [Dict [str , Any ]] = None ,
41+ ) -> RunnerResult :
3642 """
37- Invoke the LangChain model with an array of messages.
38-
39- :param messages: Array of LDMessage objects representing the conversation
40- :return: ModelResponse containing the model's response and metrics
43+ Run the LangChain model with the given input.
44+
45+ :param input: A string prompt or a list of :class:`LDMessage` objects
46+ :param output_type: Optional JSON schema dict requesting structured output.
47+ When provided, ``parsed`` on the returned :class:`RunnerResult` is
48+ populated with the structured data.
49+ :return: :class:`RunnerResult` containing ``content``, ``metrics``,
50+ ``raw`` and (when ``output_type`` is set) ``parsed``.
4151 """
52+ messages = self ._coerce_input (input )
53+ if output_type is not None :
54+ return await self ._run_structured (messages , output_type )
55+ return await self ._run_completion (messages )
56+
57+ @staticmethod
58+ def _coerce_input (input : Any ) -> List [LDMessage ]:
59+ if isinstance (input , str ):
60+ return [LDMessage (role = 'user' , content = input )]
61+ if isinstance (input , list ):
62+ return input
63+ raise TypeError (
64+ f"Unsupported input type for LangChainModelRunner.run: { type (input ).__name__ } "
65+ )
66+
67+ async def _run_completion (self , messages : List [LDMessage ]) -> RunnerResult :
4268 try :
4369 langchain_messages = convert_messages_to_langchain (messages )
4470 response : BaseMessage = await self ._llm .ainvoke (langchain_messages )
@@ -52,58 +78,58 @@ async def invoke_model(self, messages: List[LDMessage]) -> ModelResponse:
5278 f'Multimodal response not supported, expecting a string. '
5379 f'Content type: { type (response .content )} , Content: { response .content } '
5480 )
55- metrics = LDAIMetrics (success = False , usage = metrics .usage )
81+ return RunnerResult (
82+ content = '' ,
83+ metrics = LDAIMetrics (success = False , usage = metrics .usage ),
84+ raw = response ,
85+ )
5686
57- return ModelResponse (
58- message = LDMessage (role = 'assistant' , content = content ),
59- metrics = metrics ,
60- )
87+ return RunnerResult (content = content , metrics = metrics , raw = response )
6188 except Exception as error :
6289 log .warning (f'LangChain model invocation failed: { error } ' )
63- return ModelResponse (
64- message = LDMessage ( role = 'assistant' , content = '' ) ,
90+ return RunnerResult (
91+ content = '' ,
6592 metrics = LDAIMetrics (success = False , usage = None ),
6693 )
6794
68- async def invoke_structured_model (
69- self ,
70- messages : List [LDMessage ],
71- response_structure : Dict [str , Any ],
72- ) -> StructuredResponse :
73- """
74- Invoke the LangChain model with structured output support.
75-
76- :param messages: Array of LDMessage objects representing the conversation
77- :param response_structure: Dictionary defining the output structure
78- :return: StructuredResponse containing the structured data
79- """
80- structured_response = StructuredResponse (
81- data = {},
82- raw_response = '' ,
83- metrics = LDAIMetrics (success = False , usage = None ),
84- )
95+ async def _run_structured (
96+ self , messages : List [LDMessage ], response_structure : Dict [str , Any ]
97+ ) -> RunnerResult :
8598 try :
8699 langchain_messages = convert_messages_to_langchain (messages )
87100 structured_llm = self ._llm .with_structured_output (response_structure , include_raw = True )
88101 response = await structured_llm .ainvoke (langchain_messages )
89102
90103 if not isinstance (response , dict ):
91104 log .warning (f'Structured output did not return a dict. Got: { type (response )} ' )
92- return structured_response
105+ return RunnerResult (
106+ content = '' ,
107+ metrics = LDAIMetrics (success = False , usage = None ),
108+ )
93109
94110 raw_response = response .get ('raw' )
95- if raw_response is not None :
96- if hasattr (raw_response , 'content' ):
97- structured_response .raw_response = raw_response .content
98- structured_response .metrics .usage = get_ai_usage_from_response (raw_response )
111+ usage = get_ai_usage_from_response (raw_response ) if raw_response is not None else None
112+ raw_content = raw_response .content if raw_response is not None and hasattr (raw_response , 'content' ) else ''
99113
100114 if response .get ('parsing_error' ):
101115 log .warning ('LangChain structured model invocation had a parsing error' )
102- return structured_response
116+ return RunnerResult (
117+ content = raw_content ,
118+ metrics = LDAIMetrics (success = False , usage = usage ),
119+ raw = raw_response ,
120+ )
103121
104- structured_response .metrics .success = True
105- structured_response .data = response .get ('parsed' ) or {}
106- return structured_response
122+ parsed = response .get ('parsed' ) or {}
123+ return RunnerResult (
124+ content = raw_content ,
125+ metrics = LDAIMetrics (success = True , usage = usage ),
126+ raw = raw_response ,
127+ parsed = parsed ,
128+ )
107129 except Exception as error :
108130 log .warning (f'LangChain structured model invocation failed: { error } ' )
109- return structured_response
131+ return RunnerResult (
132+ content = '' ,
133+ metrics = LDAIMetrics (success = False , usage = None ),
134+ )
135+
0 commit comments