Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7ac5a24

Browse files
acdded test scripts and as_message_state and to_message_state utilities
1 parentcbaea35 commit7ac5a24

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

‎libs/core/langchain_core/runnables/utils.py‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Protocol,
1919
TypeGuard,
2020
TypeVar,
21+
Dict
2122
)
2223

2324
fromtyping_extensionsimportoverride
@@ -36,7 +37,9 @@
3637
)
3738
fromcontextvarsimportContext
3839

40+
fromlangchain_core.runnablesimportRunnableLambda
3941
fromlangchain_core.runnables.schemaimportStreamEvent
42+
fromlangchain_core.messagesimportBaseMessage,HumanMessage
4043

4144
Input=TypeVar("Input",contravariant=True)# noqa: PLC0105
4245
# Output type should implement __concat__, as eg str, list, dict do
@@ -149,6 +152,48 @@ def coro_with_context(
149152
returncoro
150153

151154

155+
defto_message_state(obj:Any)->Dict[str,list[BaseMessage]]:
156+
"""Convert any supported input into a message-state dict: {'messages': [BaseMessage, ...]}.
157+
158+
Args:
159+
obj: The input to convert.
160+
161+
Returns:
162+
A dictionary with a 'messages' key containing a list of BaseMessages.
163+
"""
164+
# None
165+
ifobjisNone:
166+
return {"messages": []}
167+
# String → assume HumanMessage
168+
ifisinstance(obj,str):
169+
return {"messages": [HumanMessage(content=obj)]}
170+
# Already a BaseMessage (could be HumanMessage, AIMessage, SystemMessage, etc.)
171+
ifisinstance(obj,BaseMessage):
172+
return {"messages": [obj]}
173+
# List of items → flatten convert each
174+
ifisinstance(obj,list):
175+
msgs:list[BaseMessage]= []
176+
foriteminobj:
177+
state=to_message_state(item)
178+
msgs.extend(state["messages"])
179+
return {"messages":msgs}
180+
# Already a dict in message state shape
181+
ifisinstance(obj,dict)and"messages"inobj:
182+
# Optionally: validate that each element is a BaseMessage
183+
returnobj
184+
# Unsupported type
185+
raiseTypeError(f"Unsupported type for to_message_state:{type(obj)}")
186+
187+
188+
defas_message_state()->RunnableLambda:
189+
"""Runnable that normalizes output to message-state structure.
190+
191+
Returns:
192+
A Runnable Lambda that normalizes output to message-state structure.
193+
"""
194+
returnRunnableLambda(lambdax:to_message_state(x))
195+
196+
152197
classIsLocalDict(ast.NodeVisitor):
153198
"""Check if a name is a local dict."""
154199

‎libs/core/tests/unit_tests/runnables/test_utils.py‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
get_function_nonlocals,
88
get_lambda_source,
99
indent_lines_after_first,
10+
as_message_state,
11+
to_message_state
12+
)
13+
fromlangchain_core.messagesimport (
14+
BaseMessage,
15+
HumanMessage
1016
)
1117

1218

@@ -72,3 +78,88 @@ def my_func6(value: str) -> str:
7278
assertRunnableLambda(my_func3).deps== [agent]
7379
assertRunnableLambda(my_func4).deps== [global_agent]
7480
assertRunnableLambda(func).deps== [nl]
81+
82+
83+
deftest_to_message_state_none()->None:
84+
result=to_message_state(None)
85+
assertresult== {"messages": []}
86+
87+
88+
deftest_to_message_state_string()->None:
89+
result=to_message_state("hello")
90+
assertisinstance(result["messages"][0],HumanMessage)
91+
assertresult== {"messages": [HumanMessage(content="hello")]}
92+
93+
94+
deftest_to_message_state_human_message()->None:
95+
msg=HumanMessage(content="hey")
96+
result=to_message_state(msg)
97+
assertresult== {"messages": [msg]}
98+
99+
100+
deftest_to_message_state_ai_message()->None:
101+
msg=AIMessage(content="ok")
102+
result=to_message_state(msg)
103+
assertresult== {"messages": [msg]}
104+
105+
106+
deftest_to_message_state_system_message()->None:
107+
msg=SystemMessage(content="sys state")
108+
result=to_message_state(msg)
109+
assertresult== {"messages": [msg]}
110+
111+
112+
deftest_to_message_state_list_of_mixed_items()->None:
113+
items= [HumanMessage(content="h"),"a",AIMessage(content="x")]
114+
result=to_message_state(items)
115+
messages=result["messages"]
116+
assertisinstance(messages[0],HumanMessage)
117+
assertisinstance(messages[1],HumanMessage)
118+
assertisinstance(messages[2],AIMessage)
119+
assertmessages[0].content=="h"
120+
assertmessages[1].content=="a"
121+
assertmessages[2].content=="x"
122+
123+
124+
deftest_to_message_state_existing_message_state_dict()->None:
125+
msg=HumanMessage(content="ok")
126+
result=to_message_state({"messages": [msg]})
127+
assertresult== {"messages": [msg]}
128+
129+
130+
deftest_to_message_state_invalid_type()->None:
131+
withpytest.raises(TypeError):
132+
to_message_state(123)# type: ignore[arg-type]
133+
134+
135+
deftest_as_message_state_wraps_string()->None:
136+
chain=as_message_state()
137+
result=chain.invoke("hello")
138+
assertresult== {"messages": [HumanMessage(content="hello")]}
139+
140+
141+
deftest_as_message_state_wraps_ai_message()->None:
142+
chain=as_message_state()
143+
result=chain.invoke(AIMessage(content="ok"))
144+
assertresult== {"messages": [AIMessage(content="ok")]}
145+
146+
147+
deftest_as_message_state_with_preceding_lc_chain()->None:
148+
llm_output=AIMessage(content="done")
149+
150+
deffake_llm(_:str)->BaseMessage:
151+
returnllm_output
152+
153+
chain=RunnableLambda(fake_llm)|as_message_state()
154+
result=chain.invoke("hello")
155+
assertresult== {"messages": [llm_output]}
156+
157+
158+
deftest_as_message_state_with_list_input()->None:
159+
chain=as_message_state()
160+
items= ["a","b",AIMessage(content="x")]
161+
result=chain.invoke(items)
162+
msgs=result["messages"]
163+
assertmsgs[0].content=="a"
164+
assertmsgs[1].content=="b"
165+
assertmsgs[2].content=="x"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp