"LangChain provides a callbacks system that allows you to hook into the various stages of your LLM application. This is useful for logging, [monitoring](https://python.langchain.com/en/latest/tracing.html), [streaming](https://python.langchain.com/en/latest/modules/models/llms/examples/streaming_llm.html), and other tasks.\n",
"You can subscribe to these events by using the `callbacks` argument available throughout the API. This argument is list of handler objects, which are expected to implement one or more of the methods described below in more detail. There are two main callbacks mechanisms:\n",
"\n",
"* *Constructor callbacks* will be used for all calls made on that object, and will be scoped to that object only, i.e. if you pass a handler to the `LLMChain` constructor, it will not be used by the model attached to that chain. \n",
"* *Request callbacks* will be used for that specific request only, and all sub-requests that it contains (eg. a call to an `LLMChain` triggers a call to a Model, which uses the same handler passed through). These are explicitly passed through.\n",
"\n",
"\n",
"**Advanced:** When you create a custom chain you can easily set it up to use the same callback system as all the built-in chains. \n",
"`_call`, `_generate`, `_run`, and equivalent async methods on Chains / LLMs / Chat Models / Agents / Tools now receive a 2nd argument called `run_manager` which is bound to that run, and contains the logging methods that can be used by that object (i.e. `on_llm_new_token`). This is useful when constructing a custom chain. See this guide for more information on how to [create custom chains and use callbacks inside them.](https://python.langchain.com/en/latest/modules/chains/generic/custom_chain.html)"
"`CallbackHandlers` are objects that implement the `CallbackHandler` interface, which has a method for each event that can be subscribed to. The `CallbackManager` will call the appropriate method on each handler when the event is triggered."
"The `callbacks` argument is available on most objects throughout the API (Chains, Models, Tools, Agents, etc.) in two different places:\n",
"\n",
"- **Constructor callbacks**: defined in the constructor, eg. `LLMChain(callbacks=[handler])`, which will be used for all calls made on that object, and will be scoped to that object only, eg. if you pass a handler to the `LLMChain` constructor, it will not be used by the Model attached to that chain.\n",
"- **Request callbacks**: defined in the `call()`/`run()`/`apply()` methods used for issuing a request, eg. `chain.call(inputs, callbacks=[handler])`, which will be used for that specific request only, and all sub-requests that it contains (eg. a call to an LLMChain triggers a call to a Model, which uses the same handler passed in the `call()` method).\n",
"\n",
"The `verbose` argument is available on most objects throughout the API (Chains, Models, Tools, Agents, etc.) as a constructor argument, eg. `LLMChain(verbose=True)`, and it is equivalent to passing a `ConsoleCallbackHandler` to the `callbacks` argument of that object and all child objects. This is useful for debugging, as it will log all events to the console.\n",
"\n",
"### When do you want to use each of these?\n",
"\n",
"- Constructor callbacks are most useful for use cases such as logging, monitoring, etc., which are _not specific to a single request_, but rather to the entire chain. For example, if you want to log all the requests made to an LLMChain, you would pass a handler to the constructor.\n",
"- Request callbacks are most useful for use cases such as streaming, where you want to stream the output of a single request to a specific websocket connection, or other similar use cases. For example, if you want to stream the output of a single request to a websocket, you would pass a handler to the `call()` method"
"LangChain provides a few built-in handlers that you can use to get started. These are available in the `langchain/callbacks` module. The most basic handler is the `StdOutCallbackHandler`, which simply logs all events to `stdout`. In the future we will add more default handlers to the library. \n",
"chat([HumanMessage(content=\"Tell me a joke\")])"
]
},
{
"cell_type": "markdown",
"id": "bc9785fa-4f71-4797-91a3-4fe7e57d0429",
"metadata": {
"tags": []
},
"source": [
"## Async Callbacks\n",
"\n",
"If you are planning to use the async API, it is recommended to use `AsyncCallbackHandler` to avoid blocking the runloop. \n",
"\n",
"**Advanced** if you use a sync `CallbackHandler` while using an async method to run your llm/chain/tool/agent, it will still work. However, under the hood, it will be called with [`run_in_executor`](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor) which can cause issues if your `CallbackHandler` is not thread-safe."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "c702e0c9-a961-4897-90c1-cdd13b6f16b2",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"zzzz....\n",
"Hi! I just woke up. Your llm is starting\n",
"Sync handler being called in a `thread_pool_executor`: token: \n",
"Sync handler being called in a `thread_pool_executor`: token: Why\n",
"Sync handler being called in a `thread_pool_executor`: token: don\n",
"Sync handler being called in a `thread_pool_executor`: token: 't\n",
"Sync handler being called in a `thread_pool_executor`: token: scientists\n",
"Sync handler being called in a `thread_pool_executor`: token: trust\n",
"Sync handler being called in a `thread_pool_executor`: token: atoms\n",
"Sync handler being called in a `thread_pool_executor`: token: ?\n",
"\n",
"\n",
"Sync handler being called in a `thread_pool_executor`: token: Because\n",
"Sync handler being called in a `thread_pool_executor`: token: they\n",
"Sync handler being called in a `thread_pool_executor`: token: make\n",
"Sync handler being called in a `thread_pool_executor`: token: up\n",
"Sync handler being called in a `thread_pool_executor`: token: everything\n",
"Sync handler being called in a `thread_pool_executor`: token: !\n",
"Sync handler being called in a `thread_pool_executor`: token: \n",
"zzzz....\n",
"Hi! I just woke up. Your llm is ending\n"
]
},
{
"data": {
"text/plain": [
"LLMResult(generations=[[ChatGeneration(text=\"Why don't scientists trust atoms?\\n\\nBecause they make up everything!\", generation_info=None, message=AIMessage(content=\"Why don't scientists trust atoms?\\n\\nBecause they make up everything!\", additional_kwargs={}))]], llm_output={'token_usage': {}, 'model_name': 'gpt-3.5-turbo'})"
"await chat.agenerate([[HumanMessage(content=\"Tell me a joke\")]])"
]
},
{
"cell_type": "markdown",
"id": "d26dbb34-fcc3-401c-a115-39c7620d2d65",
"metadata": {},
"source": [
"## Using multiple handlers, passing in handlers\n",
"\n",
"In the previous examples, we passed in callback handlers upon creation of an object by using `callbacks=`. In this case, the callbacks will be scoped to that particular object. \n",
"\n",
"However, in many cases, it is advantageous to pass in handlers instead when running the object. When we pass through `CallbackHandlers` using the `callbacks` keyword arg when executing an run, those callbacks will be issued by all nested objects involved in the execution. For example, when a handler is passed through to an `Agent`, it will be used for all callbacks related to the agent and all the objects involved in the agent's execution, in this case, the `Tools`, `LLMChain`, and `LLM`.\n",
"\n",
"This prevents us from having to manually attach the handlers to each individual nested object."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "8eec8756-1828-45cb-9699-38ac8543a150",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"on_chain_start AgentExecutor\n",
"on_chain_start LLMChain\n",
"on_llm_start OpenAI\n",
"on_llm_start (I'm the second handler!!) OpenAI\n",
"on_new_token I\n",
"on_new_token need\n",
"on_new_token to\n",
"on_new_token use\n",
"on_new_token a\n",
"on_new_token calculator\n",
"on_new_token to\n",
"on_new_token solve\n",
"on_new_token this\n",
"on_new_token .\n",
"on_new_token \n",
"Action\n",
"on_new_token :\n",
"on_new_token Calculator\n",
"on_new_token \n",
"Action\n",
"on_new_token Input\n",
"on_new_token :\n",
"on_new_token 2\n",
"on_new_token ^\n",
"on_new_token 0\n",
"on_new_token .\n",
"on_new_token 235\n",
"on_new_token \n",
"on_agent_action AgentAction(tool='Calculator', tool_input='2^0.235', log=' I need to use a calculator to solve this.\\nAction: Calculator\\nAction Input: 2^0.235')\n",
"on_tool_start Calculator\n",
"on_chain_start LLMMathChain\n",
"on_chain_start LLMChain\n",
"on_llm_start OpenAI\n",
"on_llm_start (I'm the second handler!!) OpenAI\n",
"on_new_token \n",
"\n",
"on_new_token ```text\n",
"on_new_token \n",
"\n",
"on_new_token 2\n",
"on_new_token **\n",
"on_new_token 0\n",
"on_new_token .\n",
"on_new_token 235\n",
"on_new_token \n",
"\n",
"on_new_token ```\n",
"\n",
"on_new_token ...\n",
"on_new_token num\n",
"on_new_token expr\n",
"on_new_token .\n",
"on_new_token evaluate\n",
"on_new_token (\"\n",
"on_new_token 2\n",
"on_new_token **\n",
"on_new_token 0\n",
"on_new_token .\n",
"on_new_token 235\n",
"on_new_token \")\n",
"on_new_token ...\n",
"on_new_token \n",
"\n",
"on_new_token \n",
"on_chain_start LLMChain\n",
"on_llm_start OpenAI\n",
"on_llm_start (I'm the second handler!!) OpenAI\n",
"\u001b[32;1m\u001b[1;3m I need to find out who won the US Open men's final in 2019 and then calculate his age raised to the 0.334 power.\n",
"Action: Search\n",
"Action Input: \"US Open men's final 2019 winner\"\u001b[0m\n",
"Observation: \u001b[33;1m\u001b[1;3mRafael Nadal defeated Daniil Medvedev in the final, 7–5, 6–3, 5–7, 4–6, 6–4 to win the men's singles tennis title at the 2019 US Open. It was his fourth US ...\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I need to find out the age of the winner\n",
"Observation: \u001b[33;1m\u001b[1;3mSudeikis and Wilde's relationship ended in November 2020. Wilde was publicly served with court documents regarding child custody while she was presenting Don't Worry Darling at CinemaCon 2022. In January 2021, Wilde began dating singer Harry Styles after meeting during the filming of Don't Worry Darling.\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I need to find out Harry Styles' age.\n",
"# Both of the agent runs will be traced because the environment variable is set\n",
"agent.run(questions[0])\n",
"with tracing_enabled() as session:\n",
" assert session\n",
" agent.run(questions[1])"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "a6fd6026-dc1e-4d48-893d-3592539c7828",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3m I need to find out who won the US Open men's final in 2019 and then calculate his age raised to the 0.334 power.\n",
"Action: Search\n",
"Action Input: \"US Open men's final 2019 winner\"\u001b[0m\n",
"Observation: \u001b[33;1m\u001b[1;3mRafael Nadal defeated Daniil Medvedev in the final, 7–5, 6–3, 5–7, 4–6, 6–4 to win the men's singles tennis title at the 2019 US Open. It was his fourth US ...\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I need to find out the age of the winner\n",
"Observation: \u001b[33;1m\u001b[1;3mSudeikis and Wilde's relationship ended in November 2020. Wilde was publicly served with court documents regarding child custody while she was presenting Don't Worry Darling at CinemaCon 2022. In January 2021, Wilde began dating singer Harry Styles after meeting during the filming of Don't Worry Darling.\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I need to find out Harry Styles' age.\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\n",
"\u001b[32;1m\u001b[1;3m I need to find out who won the grand prix and then calculate their age raised to the 0.23 power.\n",
"Action: Search\n",
"Action Input: \"Formula 1 Grand Prix Winner\"\u001b[0m\u001b[32;1m\u001b[1;3m I need to find out who won the US Open men's final in 2019 and then calculate his age raised to the 0.334 power.\n",
"Action: Search\n",
"Action Input: \"US Open men's final 2019 winner\"\u001b[0m\u001b[33;1m\u001b[1;3mRafael Nadal defeated Daniil Medvedev in the final, 7–5, 6–3, 5–7, 4–6, 6–4 to win the men's singles tennis title at the 2019 US Open. It was his fourth US ...\u001b[0m\u001b[32;1m\u001b[1;3m I need to find out who Olivia Wilde's boyfriend is and then calculate his age raised to the 0.23 power.\n",
"Action: Search\n",
"Action Input: \"Olivia Wilde boyfriend\"\u001b[0m\u001b[33;1m\u001b[1;3mSudeikis and Wilde's relationship ended in November 2020. Wilde was publicly served with court documents regarding child custody while she was presenting Don't Worry Darling at CinemaCon 2022. In January 2021, Wilde began dating singer Harry Styles after meeting during the filming of Don't Worry Darling.\u001b[0m\u001b[33;1m\u001b[1;3mLewis Hamilton has won 103 Grands Prix during his career. He won 21 races with McLaren and has won 82 with Mercedes. Lewis Hamilton holds the record for the ...\u001b[0m\u001b[32;1m\u001b[1;3m I need to find out the age of the winner\n",
"Action: Search\n",
"Action Input: \"Rafael Nadal age\"\u001b[0m\u001b[33;1m\u001b[1;3m36 years\u001b[0m\u001b[32;1m\u001b[1;3m I need to find out Harry Styles' age.\n",
"Action: Search\n",
"Action Input: \"Harry Styles age\"\u001b[0m\u001b[32;1m\u001b[1;3m I need to find out Lewis Hamilton's age\n",
"Action: Search\n",
"Action Input: \"Lewis Hamilton Age\"\u001b[0m\u001b[33;1m\u001b[1;3m29 years\u001b[0m\u001b[32;1m\u001b[1;3m I need to calculate the age raised to the 0.334 power\n",
"Action: Calculator\n",
"Action Input: 36^0.334\u001b[0m\u001b[32;1m\u001b[1;3m I need to calculate 29 raised to the 0.23 power.\n",