mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-22 23:52:23 +02:00
17 KiB
17 KiB
In [11]:
from operator import itemgetter
from langchain.chat_models.openai import ChatOpenAI
from langchain.prompts import (
SystemMessagePromptTemplate,
ChatPromptTemplate,
PromptTemplate,
)
from langchain.schema.output_parser import StrOutputParser
from langchain.runnables.openai_functions import OpenAIFunctionsRouter
from langchain.schema.runnable import RunnableMap, RunnablePassthrough
from langchain.schema.document import Document
from langchain.schema import format_document
from permchain import Pregel, channelsIn [2]:
from langchain.schema.runnable import RunnableLambdaIn [3]:
DEFAULT_DOCUMENT_PROMPT = PromptTemplate.from_template(template="{page_content}")
_combine_documents = RunnableLambda(
lambda x: format_document(x, DEFAULT_DOCUMENT_PROMPT)
).map() | (lambda x: "\n\n".join(x))In [4]:
docs = [
Document(page_content="Harrison used to work at Kensho"),
Document(page_content="Ankush worked at Facebook"),
]In [5]:
stuff_chain = (
{
"question": lambda x: x["question"],
"context": (lambda x: x["docs"]) | _combine_documents,
}
| ChatPromptTemplate.from_messages(
[
(
"system",
"Answer user questions based on the following documents:\n\n{context}",
),
("human", "{question}"),
]
)
| ChatOpenAI()
)In [6]:
stuff_chain.invoke({"question": "where did harrison work", "docs": docs})Out [6]:
AIMessage(content='Harrison used to work at Kensho.')
In [7]:
many_docs = docs * 5In [8]:
def _split_list_of_docs(docs, max_length=70):
new_result_doc_list = []
_sub_result_docs = []
for doc in docs:
_sub_result_docs.append(doc)
_num_tokens = sum([len(d.page_content) for d in _sub_result_docs])
if _num_tokens > max_length:
if len(_sub_result_docs) == 1:
raise ValueError(
"A single document was longer than the context length,"
" we cannot handle this."
)
new_result_doc_list.append(_sub_result_docs[:-1])
_sub_result_docs = _sub_result_docs[-1:]
new_result_doc_list.append(_sub_result_docs)
return new_result_doc_listIn [9]:
# Just to show what its like split
split_docs = _split_list_of_docs(many_docs)
split_docsOut [9]:
[[Document(page_content='Harrison used to work at Kensho'), Document(page_content='Ankush worked at Facebook')], [Document(page_content='Harrison used to work at Kensho'), Document(page_content='Ankush worked at Facebook')], [Document(page_content='Harrison used to work at Kensho'), Document(page_content='Ankush worked at Facebook')], [Document(page_content='Harrison used to work at Kensho'), Document(page_content='Ankush worked at Facebook')], [Document(page_content='Harrison used to work at Kensho'), Document(page_content='Ankush worked at Facebook')]]
In [10]:
input_inbox = channels.LastValue[str]("input_inbox")
reduce_inbox = channels.LastValue[str]("reduce_inbox")
collapse_inbox = channels.LastValue[str]("collapse_inbox")
output_inbox = channels.LastValue[str]("output_inbox")[0;31m---------------------------------------------------------------------------[0m [0;31mTypeError[0m Traceback (most recent call last) Cell [0;32mIn[10], line 1[0m [0;32m----> 1[0m input_inbox [38;5;241m=[39m [43mchannels[49m[38;5;241;43m.[39;49m[43mLastValue[49m[43m[[49m[38;5;28;43mstr[39;49m[43m][49m[43m([49m[38;5;124;43m"[39;49m[38;5;124;43minput_inbox[39;49m[38;5;124;43m"[39;49m[43m)[49m [1;32m 2[0m reduce_inbox [38;5;241m=[39m channels[38;5;241m.[39mLastValue[[38;5;28mstr[39m]([38;5;124m"[39m[38;5;124mreduce_inbox[39m[38;5;124m"[39m) [1;32m 3[0m collapse_inbox [38;5;241m=[39m channels[38;5;241m.[39mLastValue[[38;5;28mstr[39m]([38;5;124m"[39m[38;5;124mcollapse_inbox[39m[38;5;124m"[39m) File [0;32m/opt/homebrew/Cellar/python@3.11/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/typing.py:1268[0m, in [0;36m_BaseGenericAlias.__call__[0;34m(self, *args, **kwargs)[0m [1;32m 1265[0m [38;5;28;01mif[39;00m [38;5;129;01mnot[39;00m [38;5;28mself[39m[38;5;241m.[39m_inst: [1;32m 1266[0m [38;5;28;01mraise[39;00m [38;5;167;01mTypeError[39;00m([38;5;124mf[39m[38;5;124m"[39m[38;5;124mType [39m[38;5;132;01m{[39;00m[38;5;28mself[39m[38;5;241m.[39m_name[38;5;132;01m}[39;00m[38;5;124m cannot be instantiated; [39m[38;5;124m"[39m [1;32m 1267[0m [38;5;124mf[39m[38;5;124m"[39m[38;5;124muse [39m[38;5;132;01m{[39;00m[38;5;28mself[39m[38;5;241m.[39m__origin__[38;5;241m.[39m[38;5;18m__name__[39m[38;5;132;01m}[39;00m[38;5;124m() instead[39m[38;5;124m"[39m) [0;32m-> 1268[0m result [38;5;241m=[39m [38;5;28;43mself[39;49m[38;5;241;43m.[39;49m[43m__origin__[49m[43m([49m[38;5;241;43m*[39;49m[43margs[49m[43m,[49m[43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m)[49m [1;32m 1269[0m [38;5;28;01mtry[39;00m: [1;32m 1270[0m result[38;5;241m.[39m__orig_class__ [38;5;241m=[39m [38;5;28mself[39m [0;31mTypeError[0m: LastValue() takes no arguments
In [22]:
# Decide if should finish or should reduce one more step
def decide_end(plan):
if len(plan["docs"]) > 1:
return Pregel.send_to("reduce_inbox")
else:
return stuff_chain | Pregel.send_to("output_inbox")
# Chain that collapses documents then chooses end
collapse_chain = (
Pregel.subscribe_to(docs=collapse_inbox, question="question")
| RunnablePassthrough.assign(docs=lambda x: _split_list_of_docs(x["docs"]))
| decide_end
)
reduce_chain = (
Pregel.subscribe_to(input=input_inbox)
| (lambda x: [{"docs": d, "question": x["question"]} for d in x["docs"]])
| stuff_chain.map()
| Pregel.send_to(
{
"collapse_inbox": {
"docs": lambda x: [Document(page_content=m.content) for m in x],
}
}
)
)In [23]:
pubsub = Pregel(
input_inbox, reduce_inbox, collapse_inbox, input=input_inbox, output=output_inbox
)[0;31m---------------------------------------------------------------------------[0m [0;31mValidationError[0m Traceback (most recent call last) Cell [0;32mIn[23], line 1[0m [0;32m----> 1[0m pubsub [38;5;241m=[39m [43mPregel[49m[43m([49m[43minput_inbox[49m[43m,[49m[43m [49m[43mreduce_inbox[49m[43m,[49m[43m [49m[43mcollapse_inbox[49m[43m,[49m[43m [49m[38;5;28;43minput[39;49m[38;5;241;43m=[39;49m[43minput_inbox[49m[43m,[49m[43m [49m[43moutput[49m[38;5;241;43m=[39;49m[43moutput_inbox[49m[43m)[49m File [0;32m~/workplace/permchain/permchain/pregel.py:244[0m, in [0;36mPregel.__init__[0;34m(self, input, output, step_timeout, *processes, **kwargs)[0m [1;32m 236[0m [38;5;28;01mdef[39;00m [38;5;21m__init__[39m( [1;32m 237[0m [38;5;28mself[39m, [1;32m 238[0m [38;5;241m*[39mprocesses: PregelInvoke [38;5;241m|[39m PregelBatch, [0;32m (...)[0m [1;32m 242[0m [38;5;241m*[39m[38;5;241m*[39mkwargs: Any, [1;32m 243[0m ): [0;32m--> 244[0m [38;5;28;43msuper[39;49m[43m([49m[43m)[49m[38;5;241;43m.[39;49m[38;5;21;43m__init__[39;49m[43m([49m [1;32m 245[0m [43m [49m[43mprocesses[49m[38;5;241;43m=[39;49m[43mprocesses[49m[43m,[49m [1;32m 246[0m [43m [49m[38;5;28;43minput[39;49m[38;5;241;43m=[39;49m[38;5;28;43minput[39;49m[43m,[49m [1;32m 247[0m [43m [49m[43moutput[49m[38;5;241;43m=[39;49m[43moutput[49m[43m,[49m [1;32m 248[0m [43m [49m[43mstep_timeout[49m[38;5;241;43m=[39;49m[43mstep_timeout[49m[43m,[49m [1;32m 249[0m [43m [49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m,[49m [1;32m 250[0m [43m [49m[43m)[49m File [0;32m~/.pyenv/versions/3.10.1/envs/permchain/lib/python3.10/site-packages/langchain/load/serializable.py:90[0m, in [0;36mSerializable.__init__[0;34m(self, **kwargs)[0m [1;32m 89[0m [38;5;28;01mdef[39;00m [38;5;21m__init__[39m([38;5;28mself[39m, [38;5;241m*[39m[38;5;241m*[39mkwargs: Any) [38;5;241m-[39m[38;5;241m>[39m [38;5;28;01mNone[39;00m: [0;32m---> 90[0m [38;5;28;43msuper[39;49m[43m([49m[43m)[49m[38;5;241;43m.[39;49m[38;5;21;43m__init__[39;49m[43m([49m[38;5;241;43m*[39;49m[38;5;241;43m*[39;49m[43mkwargs[49m[43m)[49m [1;32m 91[0m [38;5;28mself[39m[38;5;241m.[39m_lc_kwargs [38;5;241m=[39m kwargs File [0;32m~/.pyenv/versions/3.10.1/envs/permchain/lib/python3.10/site-packages/pydantic/main.py:341[0m, in [0;36mpydantic.main.BaseModel.__init__[0;34m()[0m [0;31mValidationError[0m: 6 validation errors for Pregel processes -> 0 value is not a valid dict (type=type_error.dict) processes -> 0 value is not a valid dict (type=type_error.dict) processes -> 1 value is not a valid dict (type=type_error.dict) processes -> 1 value is not a valid dict (type=type_error.dict) processes -> 2 value is not a valid dict (type=type_error.dict) processes -> 2 value is not a valid dict (type=type_error.dict)
In [101]:
reduce_agent.invoke({"question": "where did harrison work", "docs": many_docs})Out [101]:
[AIMessage(content='Harrison used to work at Kensho.', additional_kwargs={}, example=False)]In [ ]: