diff --git a/CHANGELOG.md b/CHANGELOG.md index bb9a52e..30b9692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.0] + +- Support parallel tool calling by default in `Chat`. +- Legacy support for function calling is available by passing `legacy_function_calling=True` to the `Chat` constructor. + ## [1.3.0] - Support tool call format from `FunctionRegistry`. Enables parallel function calling (note: not in `Chat` yet). https://github.com/rgbkrk/chatlab/pull/122 diff --git a/chatlab/chat.py b/chatlab/chat.py index 790d552..0d908b8 100644 --- a/chatlab/chat.py +++ b/chatlab/chat.py @@ -26,7 +26,7 @@ from ._version import __version__ from .errors import ChatLabError -from .messaging import human +from .messaging import assistant_tool_calls, human from .registry import FunctionRegistry, PythonHallucinationFunction from .views import ToolArguments, AssistantMessageView @@ -73,6 +73,7 @@ def __init__( chat_functions: Optional[List[Callable]] = None, allow_hallucinated_python: bool = False, python_hallucination_function: Optional[PythonHallucinationFunction] = None, + legacy_function_calling: bool = False, ): """Initialize a Chat with an optional initial context of messages. @@ -99,6 +100,8 @@ def __init__( self.api_key = openai_api_key self.base_url = base_url + self.legacy_function_calling = legacy_function_calling + if initial_context is None: initial_context = [] # type: ignore @@ -142,11 +145,13 @@ async def __call__(self, *messages: Union[ChatCompletionMessageParam, str], stre async def __process_stream( self, resp: AsyncStream[ChatCompletionChunk] - ) -> Tuple[str, Optional[ToolArguments]]: + ) -> Tuple[str, Optional[ToolArguments], List[ToolArguments]]: assistant_view: AssistantMessageView = AssistantMessageView() function_view: Optional[ToolArguments] = None finish_reason = None + tool_calls: list[ToolArguments] = [] + async for result in resp: # Go through the results of the stream choices = result.choices @@ -158,18 +163,50 @@ async def __process_stream( # Is stream choice? if choice.delta is not None: - if choice.delta.content is not None: + if choice.delta.content is not None and choice.delta.content != "": assistant_view.display_once() assistant_view.append(choice.delta.content) + elif choice.delta.tool_calls is not None: + if not assistant_view.finished: + assistant_view.finished = True + + if assistant_view.content != "": + # Flush out the finished assistant message + message = assistant_view.get_message() + self.append(message) + for tool_call in choice.delta.tool_calls: + if tool_call.function is None: + # This should not be occurring. We could continue instead. + raise ValueError("Tool call without function") + # If this is a continuation of a tool call, then we have to change the tool argument + if tool_call.index < len(tool_calls): + tool_argument = tool_calls[tool_call.index] + if tool_call.function.arguments is not None: + tool_argument.append_arguments(tool_call.function.arguments) + elif ( + tool_call.function.name is not None + and tool_call.function.arguments is not None + and tool_call.id is not None + ): + # Must build up + tool_argument = ToolArguments( + id=tool_call.id, name=tool_call.function.name, arguments=tool_call.function.arguments + ) + tool_argument.display() + tool_calls.append(tool_argument) + elif choice.delta.function_call is not None: function_call = choice.delta.function_call if function_call.name is not None: if not assistant_view.finished: - # Flush out the finished assistant message - message = assistant_view.get_message() - self.append(message) + assistant_view.finished = True + if assistant_view.content != "": + # Flush out the finished assistant message + message = assistant_view.get_message() + self.append(message) + # IDs are for the tool calling apparatus from newer versions of the API - # We will make use of it later. + # Function call just uses the name. It's 1:1, whereas tools allow for multiple calls. function_view = ToolArguments(id="TBD", name=function_call.name) function_view.display() if function_call.arguments is not None: @@ -189,15 +226,19 @@ async def __process_stream( if finish_reason is None: raise ValueError("No finish reason provided by OpenAI") - return (finish_reason, function_view) + return (finish_reason, function_view, tool_calls) - async def __process_full_completion(self, resp: ChatCompletion) -> Tuple[str, Optional[ToolArguments]]: + async def __process_full_completion( + self, resp: ChatCompletion + ) -> Tuple[str, Optional[ToolArguments], List[ToolArguments]]: assistant_view: AssistantMessageView = AssistantMessageView() function_view: Optional[ToolArguments] = None + tool_calls: list[ToolArguments] = [] + if len(resp.choices) == 0: logger.warning(f"Result has no choices: {resp}") - return ("stop", None) # TODO + return ("stop", None, tool_calls) # TODO choice = resp.choices[0] @@ -211,8 +252,18 @@ async def __process_full_completion(self, resp: ChatCompletion) -> Tuple[str, Op function_call = message.function_call function_view = ToolArguments(id="TBD", name=function_call.name, arguments=function_call.arguments) function_view.display() + if message.tool_calls is not None: + for tool_call in message.tool_calls: + tool_argument = ToolArguments( + id=tool_call.id, name=tool_call.function.name, arguments=tool_call.function.arguments + ) + tool_argument.display() + tool_calls.append(tool_argument) + + # TODO: self.append the big tools payload, verify this + self.append(message.model_dump()) # type: ignore - return choice.finish_reason, function_view + return choice.finish_reason, function_view, tool_calls async def submit(self, *messages: Union[ChatCompletionMessageParam, str], stream=True, **kwargs): """Send messages to the chat model and display the response. @@ -231,6 +282,10 @@ async def submit(self, *messages: Union[ChatCompletionMessageParam, str], stream """ full_messages: List[ChatCompletionMessageParam] = [] full_messages.extend(self.messages) + + # TODO: Just keeping this aside while working on both stream and non-stream + tool_arguments: List[ToolArguments] = [] + for message in messages: if isinstance(message, str): full_messages.append(human(message)) @@ -243,37 +298,39 @@ async def submit(self, *messages: Union[ChatCompletionMessageParam, str], stream base_url=self.base_url, ) - api_manifest = self.function_registry.api_manifest() + chat_create_kwargs = { + "model": self.model, + "messages": full_messages, + "temperature": kwargs.get("temperature", 0), + } # Due to the strict response typing based on `Literal` typing on `stream`, we have to process these # two cases separately if stream: + if self.legacy_function_calling: + chat_create_kwargs.update(self.function_registry.api_manifest()) + else: + chat_create_kwargs["tools"] = self.function_registry.tools + streaming_response = await client.chat.completions.create( - model=self.model, - messages=full_messages, - **api_manifest, + **chat_create_kwargs, stream=True, - temperature=kwargs.get("temperature", 0), ) self.append(*messages) - finish_reason, function_call_request = await self.__process_stream(streaming_response) + finish_reason, function_call_request, tool_arguments = await self.__process_stream(streaming_response) else: full_response = await client.chat.completions.create( - model=self.model, - messages=full_messages, - **api_manifest, + **chat_create_kwargs, stream=False, - temperature=kwargs.get("temperature", 0), ) self.append(*messages) - ( - finish_reason, - function_call_request, - ) = await self.__process_full_completion(full_response) + (finish_reason, function_call_request, tool_arguments) = await self.__process_full_completion( + full_response + ) except openai.RateLimitError as e: logger.error(f"Rate limited: {e}. Waiting 5 seconds and trying again.") @@ -299,6 +356,17 @@ async def submit(self, *messages: Union[ChatCompletionMessageParam, str], stream await self.submit(stream=stream, **kwargs) return + if finish_reason == "tool_calls": + self.append(assistant_tool_calls(tool_arguments)) + for tool_argument in tool_arguments: + # Oh crap I need to append the big assistant call of it too. May have to assume we've done it by here. + function_called = await tool_argument.call(self.function_registry) + # TODO: Format the tool message + self.append(function_called.get_tool_called_message()) + + await self.submit(stream=stream, **kwargs) + return + # All other finish reasons are valid for regular assistant messages if finish_reason == "stop": return diff --git a/chatlab/messaging.py b/chatlab/messaging.py index 547e658..1c17dcb 100644 --- a/chatlab/messaging.py +++ b/chatlab/messaging.py @@ -10,9 +10,13 @@ """ -from typing import Optional +from typing import Optional, Iterable, Protocol, List -from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolMessageParam +from openai.types.chat import ( + ChatCompletionMessageParam, + ChatCompletionToolMessageParam, + ChatCompletionMessageToolCallParam, +) def assistant(content: str) -> ChatCompletionMessageParam: @@ -100,7 +104,29 @@ def function_result(name: str, content: str) -> ChatCompletionMessageParam: } -def tool_result(tool_call_id: str, content: str) -> ChatCompletionToolMessageParam: +class HasGetToolArgumentsParameter(Protocol): + def get_tool_arguments_parameter(self) -> ChatCompletionMessageToolCallParam: + ... + + +def assistant_tool_calls(tool_calls: Iterable[HasGetToolArgumentsParameter]) -> ChatCompletionMessageParam: + converted_tool_calls: List[ChatCompletionMessageToolCallParam] = [] + + for tool_call in tool_calls: + converted_tool_calls.append(tool_call.get_tool_arguments_parameter()) + + return { + "role": "assistant", + "tool_calls": converted_tool_calls, + } + + +class ChatCompletionToolMessageParamWithName(ChatCompletionToolMessageParam): + name: Optional[str] + """The name of the tool.""" + + +def tool_result(tool_call_id: str, content: str, name: str) -> ChatCompletionToolMessageParamWithName: """Create a tool result message. Args: @@ -112,10 +138,12 @@ def tool_result(tool_call_id: str, content: str) -> ChatCompletionToolMessagePar """ return { "role": "tool", + "name": name, "content": content, "tool_call_id": tool_call_id, } + # Aliases narrate = system human = user diff --git a/chatlab/registry.py b/chatlab/registry.py index 6331460..3631160 100644 --- a/chatlab/registry.py +++ b/chatlab/registry.py @@ -460,7 +460,7 @@ async def call(self, name: str, arguments: Optional[str] = None) -> Any: parameters: dict = {} - if arguments is not None: + if arguments is not None and arguments != "": try: parameters = json.loads(arguments) except json.JSONDecodeError: diff --git a/chatlab/views/assistant.py b/chatlab/views/assistant.py index 436fad4..bb3cd76 100644 --- a/chatlab/views/assistant.py +++ b/chatlab/views/assistant.py @@ -2,8 +2,9 @@ from ..messaging import assistant + class AssistantMessageView(Markdown): - content: str= "" + content: str = "" finished: bool = False has_displayed: bool = False @@ -17,5 +18,3 @@ def display(self): def display_once(self): if not self.has_displayed: self.display() - - diff --git a/chatlab/views/tools.py b/chatlab/views/tools.py index 2be94d2..02cfb0b 100644 --- a/chatlab/views/tools.py +++ b/chatlab/views/tools.py @@ -4,10 +4,14 @@ from ..registry import FunctionRegistry, FunctionArgumentError, UnknownFunctionError -from ..messaging import assistant_function_call, function_result - +from ..messaging import assistant_function_call, function_result, tool_result + +from openai.types.chat import ChatCompletionMessageToolCallParam + + class ToolCalled(AutoUpdate): """Once a tool has finished up, this is the view.""" + id: str name: str arguments: str = "" @@ -15,17 +19,16 @@ class ToolCalled(AutoUpdate): result: str = "" def render(self): - return ChatFunctionComponent( - name=self.name, - verbage="ok", - input=self.arguments, - output=self.result - ) - + return ChatFunctionComponent(name=self.name, verbage="ok", input=self.arguments, output=self.result) + # TODO: This is only here for legacy function calling def get_function_called_message(self): return function_result(self.name, self.result) + def get_tool_called_message(self): + # NOTE: OpenAI has mismatched types where it doesn't include the `name` + # xref: https://github.com/openai/openai-python/issues/1078 + return tool_result(tool_call_id=self.id, content=self.result, name=self.name) class ToolArguments(AutoUpdate): @@ -39,26 +42,21 @@ class ToolArguments(AutoUpdate): def get_function_message(self): return assistant_function_call(self.name, self.arguments) + def get_tool_arguments_parameter(self) -> ChatCompletionMessageToolCallParam: + return {"id": self.id, "function": {"name": self.name, "arguments": self.arguments}, "type": "function"} + def render(self): - return ChatFunctionComponent( - name=self.name, - verbage=self.verbage, - input=self.arguments - ) - + return ChatFunctionComponent(name=self.name, verbage=self.verbage, input=self.arguments) + def append_arguments(self, arguments: str): self.arguments += arguments - + def apply_result(self, result: str): """Replaces the existing display with a new one that shows the result of the tool being called.""" return ToolCalled( - id=self.id, - name=self.name, - arguments=self.arguments, - result=result, - display_id=self.display_id + id=self.id, name=self.name, arguments=self.arguments, result=result, display_id=self.display_id ) - + async def call(self, function_registry: FunctionRegistry) -> ToolCalled: """Call the function and return a stack of messages for LLM and human consumption.""" function_name = self.name @@ -113,4 +111,3 @@ async def call(self, function_registry: FunctionRegistry) -> ToolCalled: self.verbage = "Ran" return self.apply_result(repr_llm) - diff --git a/notebooks/knowledge-graph.ipynb b/notebooks/knowledge-graph.ipynb index 66d4298..02418c0 100644 --- a/notebooks/knowledge-graph.ipynb +++ b/notebooks/knowledge-graph.ipynb @@ -2,136 +2,141 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install graphviz -q" + ] + }, + { + "cell_type": "code", + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
𝑓Ranvisualize_knowledge_graph
Input:
{\n", + "
𝑓Ranvisualize_knowledge_graph...
Input:
{\n", " "kg": {\n", " "nodes": [\n", " {\n", " "id": 1,\n", - " "label": "Water",\n", - " "color": "blue"\n", + " "label": "Coffee Beans",\n", + " "color": "brown"\n", " },\n", " {\n", " "id": 2,\n", - " "label": "Coffee Grinder",\n", + " "label": "Grinder",\n", " "color": "grey"\n", " },\n", " {\n", " "id": 3,\n", - " "label": "Coffee Beans",\n", + " "label": "Ground Coffee",\n", " "color": "brown"\n", " },\n", " {\n", " "id": 4,\n", - " "label": "Grind Beans",\n", - " "color": "orange"\n", + " "label": "Water",\n", + " "color": "blue"\n", " },\n", " {\n", " "id": 5,\n", - " "label": "Coffee Filter",\n", - " "color": "white"\n", + " "label": "Heating",\n", + " "color": "orange"\n", " },\n", " {\n", " "id": 6,\n", - " "label": "Coffee Machine",\n", - " "color": "black"\n", + " "label": "Hot Water",\n", + " "color": "blue"\n", " },\n", " {\n", " "id": 7,\n", - " "label": "Heat Water",\n", - " "color": "orange"\n", + " "label": "Coffee Maker",\n", + " "color": "black"\n", " },\n", " {\n", " "id": 8,\n", - " "label": "Place Grounds in Filter",\n", - " "color": "orange"\n", + " "label": "Brewing",\n", + " "color": "black"\n", " },\n", " {\n", " "id": 9,\n", - " "label": "Pour Water into Machine",\n", - " "color": "orange"\n", + " "label": "Coffee",\n", + " "color": "brown"\n", " },\n", " {\n", " "id": 10,\n", - " "label": "Brew",\n", - " "color": "orange"\n", - " },\n", - " {\n", - " "id": 11,\n", - " "label": "Coffee",\n", - " "color": "darkbrown"\n", + " "label": "Cup",\n", + " "color": "white"\n", " }\n", " ],\n", " "edges": [\n", " {\n", - " "source": 3,\n", + " "source": 1,\n", " "target": 2,\n", " "label": "is ground by"\n", " },\n", " {\n", " "source": 2,\n", - " "target": 4,\n", - " "label": "to"\n", + " "target": 3,\n", + " "label": "produces"\n", " },\n", " {\n", " "source": 4,\n", " "target": 5,\n", - " "label": "place into"\n", + " "label": "is heated by"\n", " },\n", " {\n", " "source": 5,\n", " "target": 6,\n", - " "label": "insert into"\n", + " "label": "produces"\n", " },\n", " {\n", - " "source": 1,\n", + " "source": 3,\n", " "target": 7,\n", - " "label": "is used in"\n", + " "label": "is placed in"\n", " },\n", " {\n", - " "source": 7,\n", - " "target": 6,\n", - " "label": "pour into"\n", + " "source": 6,\n", + " "target": 7,\n", + " "label": "is poured into"\n", " },\n", " {\n", - " "source": 9,\n", - " "target": 6,\n", - " "label": "start"\n", + " "source": 7,\n", + " "target": 8,\n", + " "label": "performs"\n", " },\n", " {\n", - " "source": 6,\n", - " "target": 10,\n", - " "label": "to"\n", + " "source": 8,\n", + " "target": 9,\n", + " "label": "results in"\n", " },\n", " {\n", - " "source": 10,\n", - " "target": 11,\n", - " "label": "produce"\n", + " "source": 9,\n", + " "target": 10,\n", + " "label": "is served in"\n", " }\n", " ]\n", " },\n", " "comment": "Coffee Brewing Process"\n", - "}
Output:
<<Graphic displayed inline for the user to see>>
" + "}
" ], "text/plain": [ - "" + "ToolArguments(display_id='f72bb39d-e045-4a8e-b0bf-2a764f8c2dd9', id='call_VtdlyED4Hak06B2qje1AoRmP', name='visualize_knowledge_graph', arguments='{\\n \"kg\": {\\n \"nodes\": [\\n {\\n \"id\": 1,\\n \"label\": \"Coffee Beans\",\\n \"color\": \"brown\"\\n },\\n {\\n \"id\": 2,\\n \"label\": \"Grinder\",\\n \"color\": \"grey\"\\n },\\n {\\n \"id\": 3,\\n \"label\": \"Ground Coffee\",\\n \"color\": \"brown\"\\n },\\n {\\n \"id\": 4,\\n \"label\": \"Water\",\\n \"color\": \"blue\"\\n },\\n {\\n \"id\": 5,\\n \"label\": \"Heating\",\\n \"color\": \"orange\"\\n },\\n {\\n \"id\": 6,\\n \"label\": \"Hot Water\",\\n \"color\": \"blue\"\\n },\\n {\\n \"id\": 7,\\n \"label\": \"Coffee Maker\",\\n \"color\": \"black\"\\n },\\n {\\n \"id\": 8,\\n \"label\": \"Brewing\",\\n \"color\": \"black\"\\n },\\n {\\n \"id\": 9,\\n \"label\": \"Coffee\",\\n \"color\": \"brown\"\\n },\\n {\\n \"id\": 10,\\n \"label\": \"Cup\",\\n \"color\": \"white\"\\n }\\n ],\\n \"edges\": [\\n {\\n \"source\": 1,\\n \"target\": 2,\\n \"label\": \"is ground by\"\\n },\\n {\\n \"source\": 2,\\n \"target\": 3,\\n \"label\": \"produces\"\\n },\\n {\\n \"source\": 4,\\n \"target\": 5,\\n \"label\": \"is heated by\"\\n },\\n {\\n \"source\": 5,\\n \"target\": 6,\\n \"label\": \"produces\"\\n },\\n {\\n \"source\": 3,\\n \"target\": 7,\\n \"label\": \"is placed in\"\\n },\\n {\\n \"source\": 6,\\n \"target\": 7,\\n \"label\": \"is poured into\"\\n },\\n {\\n \"source\": 7,\\n \"target\": 8,\\n \"label\": \"performs\"\\n },\\n {\\n \"source\": 8,\\n \"target\": 9,\\n \"label\": \"results in\"\\n },\\n {\\n \"source\": 9,\\n \"target\": 10,\\n \"label\": \"is served in\"\\n }\\n ]\\n },\\n \"comment\": \"Coffee Brewing Process\"\\n}', verbage='Ran', finished=True)" ] }, "metadata": {}, "output_type": "display_data" }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Warning: darkbrown is not a known color.\n" - ] - }, { "data": { "image/svg+xml": [ @@ -141,144 +146,138 @@ "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "1\n", - "\n", - "Water\n", - "\n", - "\n", - "\n", - "7\n", - "\n", - "Heat Water\n", - "\n", - "\n", - "\n", - "1->7\n", - "\n", - "\n", - "is used in\n", + "\n", + "Coffee Beans\n", "\n", "\n", "\n", "2\n", - "\n", - "Coffee Grinder\n", - "\n", - "\n", - "\n", - "4\n", - "\n", - "Grind Beans\n", + "\n", + "Grinder\n", "\n", - "\n", - "\n", - "2->4\n", - "\n", - "\n", - "to\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "is ground by\n", "\n", "\n", "\n", "3\n", - "\n", - "Coffee Beans\n", + "\n", + "Ground Coffee\n", "\n", - "\n", - "\n", - "3->2\n", - "\n", - "\n", - "is ground by\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "produces\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "Coffee Maker\n", + "\n", + "\n", + "\n", + "3->7\n", + "\n", + "\n", + "is placed in\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "Water\n", "\n", "\n", "\n", "5\n", - "\n", - "Coffee Filter\n", + "\n", + "Heating\n", "\n", "\n", "\n", "4->5\n", - "\n", - "\n", - "place into\n", + "\n", + "\n", + "is heated by\n", "\n", "\n", "\n", "6\n", - "\n", - "Coffee Machine\n", + "\n", + "Hot Water\n", "\n", "\n", "\n", "5->6\n", - "\n", - "\n", - "insert into\n", + "\n", + "\n", + "produces\n", "\n", - "\n", - "\n", - "10\n", - "\n", - "Brew\n", - "\n", - "\n", - "\n", - "6->10\n", - "\n", - "\n", - "to\n", - "\n", - "\n", + "\n", "\n", - "7->6\n", - "\n", - "\n", - "pour into\n", + "6->7\n", + "\n", + "\n", + "is poured into\n", "\n", "\n", "\n", "8\n", - "\n", - "Place Grounds in Filter\n", + "\n", + "Brewing\n", + "\n", + "\n", + "\n", + "7->8\n", + "\n", + "\n", + "performs\n", "\n", "\n", "\n", "9\n", - "\n", - "Pour Water into Machine\n", + "\n", + "Coffee\n", "\n", - "\n", - "\n", - "9->6\n", - "\n", - "\n", - "start\n", - "\n", - "\n", - "\n", - "11\n", - "\n", - "Coffee\n", - "\n", - "\n", + "\n", + "\n", + "8->9\n", + "\n", + "\n", + "results in\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "Cup\n", + "\n", + "\n", "\n", - "10->11\n", - "\n", - "\n", - "produce\n", + "9->10\n", + "\n", + "\n", + "is served in\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -287,47 +286,26 @@ { "data": { "text/markdown": [ - "The process of brewing coffee involves several key steps and components as illustrated in the knowledge graph:\n", + "The process of brewing coffee involves the following steps, which are visually represented in this knowledge graph:\n", "\n", - "1. **Coffee Beans** are the starting material. They are whole, raw beans that need to be ground before brewing.\n", - "2. **Coffee Grinder** is used to grind the coffee beans into grounds.\n", - "3. **Grind Beans** is the process of breaking down coffee beans into smaller particles to increase the surface area for extraction.\n", - "4. **Coffee Filter** is used to hold the coffee grounds and prevent them from getting into the final brew.\n", - "5. **Coffee Machine** is the apparatus that will heat the water, pour it over the grounds, and brew the coffee.\n", - "6. **Water** is a crucial ingredient in coffee brewing.\n", - "7. **Heat Water** is the process to bring the water to the right temperature for brewing.\n", - "8. **Place Grounds in Filter** involves putting the ground coffee into the filter.\n", - "9. **Pour Water into Machine** is the action of adding water to the coffee machine for heating and brewing.\n", - "10. **Brew** is the actual process where hot water is passed through the grounds, extracting flavors and compounds to create coffee.\n", - "11. **Coffee** is the final product, a brewed beverage ready for consumption.\n", + "1. **Coffee Beans:** Start with whole coffee beans, which are the raw material for making coffee.\n", + "2. **Grinder:** The coffee beans are ground using a grinder. This process is important for the freshness and flavor of the coffee, as grinding increases the surface area and helps in better extraction of flavors.\n", + "3. **Ground Coffee:** The result of grinding the coffee beans is ground coffee, which is ready for brewing.\n", + "4. **Water:** Begin with clean water, which is essential for the taste of the coffee.\n", + "5. **Heating:** The water is then heated to the right temperature, which is typically between 195°F and 205°F (90°C to 96°C).\n", + "6. **Hot Water:** The heated water becomes hot water, ready to be used in brewing.\n", + "7. **Coffee Maker:** The ground coffee is placed in the coffee maker's filter basket or other brewing device, and the hot water is poured over it.\n", + "8. **Brewing:** The coffee maker then brews the coffee, which involves the hot water passing through the ground coffee to extract its flavors and compounds.\n", + "9. **Coffee:** The result of the brewing process is coffee, which is the beverage we enjoy.\n", + "10. **Cup:** The brewed coffee is served in a cup.\n", "\n", - "To summarize the brewing process, you start with whole coffee beans, grind them, place the grounds in a filter, insert the filter into the coffee machine, heat water, pour the water into the machine, and then start the machine to brew the coffee, resulting in the final beverage." + "This is a simplified overview of the process, and variations can occur depending on the brewing method used (e.g., drip, espresso, French press, etc.). Each step is crucial to achieving the desired flavor and strength of the coffee." ], "text/plain": [ - "The process of brewing coffee involves several key steps and components as illustrated in the knowledge graph:\n", - "\n", - "1. **Coffee Beans** are the starting material. They are whole, raw beans that need to be ground before brewing.\n", - "2. **Coffee Grinder** is used to grind the coffee beans into grounds.\n", - "3. **Grind Beans** is the process of breaking down coffee beans into smaller particles to increase the surface area for extraction.\n", - "4. **Coffee Filter** is used to hold the coffee grounds and prevent them from getting into the final brew.\n", - "5. **Coffee Machine** is the apparatus that will heat the water, pour it over the grounds, and brew the coffee.\n", - "6. **Water** is a crucial ingredient in coffee brewing.\n", - "7. **Heat Water** is the process to bring the water to the right temperature for brewing.\n", - "8. **Place Grounds in Filter** involves putting the ground coffee into the filter.\n", - "9. **Pour Water into Machine** is the action of adding water to the coffee machine for heating and brewing.\n", - "10. **Brew** is the actual process where hot water is passed through the grounds, extracting flavors and compounds to create coffee.\n", - "11. **Coffee** is the final product, a brewed beverage ready for consumption.\n", - "\n", - "To summarize the brewing process, you start with whole coffee beans, grind them, place the grounds in a filter, insert the filter into the coffee machine, heat water, pour the water into the machine, and then start the machine to brew the coffee, resulting in the final beverage." + "AssistantMessageView(display_id='3c7ab206-6a47-46bc-a5b2-b710e99d9499', content=\"The process of brewing coffee involves the following steps, which are visually represented in this knowledge graph:\\n\\n1. **Coffee Beans:** Start with whole coffee beans, which are the raw material for making coffee.\\n2. **Grinder:** The coffee beans are ground using a grinder. This process is important for the freshness and flavor of the coffee, as grinding increases the surface area and helps in better extraction of flavors.\\n3. **Ground Coffee:** The result of grinding the coffee beans is ground coffee, which is ready for brewing.\\n4. **Water:** Begin with clean water, which is essential for the taste of the coffee.\\n5. **Heating:** The water is then heated to the right temperature, which is typically between 195°F and 205°F (90°C to 96°C).\\n6. **Hot Water:** The heated water becomes hot water, ready to be used in brewing.\\n7. **Coffee Maker:** The ground coffee is placed in the coffee maker's filter basket or other brewing device, and the hot water is poured over it.\\n8. **Brewing:** The coffee maker then brews the coffee, which involves the hot water passing through the ground coffee to extract its flavors and compounds.\\n9. **Coffee:** The result of the brewing process is coffee, which is the beverage we enjoy.\\n10. **Cup:** The brewed coffee is served in a cup.\\n\\nThis is a simplified overview of the process, and variations can occur depending on the brewing method used (e.g., drip, espresso, French press, etc.). Each step is crucial to achieving the desired flavor and strength of the coffee.\", finished=False, has_displayed=True)" ] }, - "metadata": { - "text/markdown": { - "chatlab": { - "default": true - } - } - }, + "metadata": {}, "output_type": "display_data" } ], @@ -384,44 +362,28 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
𝑓Ranvisualize_knowledge_graph
Input:
{\n", - " "kg": {\n", - " "nodes": [\n", - " { "id": 1, "label": "Water", "color": "blue" },\n", - " { "id": 2, "label": "Coffee Grinder", "color": "grey" },\n", - " { "id": 3, "label": "Coffee Beans", "color": "brown" },\n", - " { "id": 4, "label": "Grind Beans", "color": "orange" },\n", - " { "id": 5, "label": "Coffee Filter", "color": "white" },\n", - " { "id": 6, "label": "Coffee Machine", "color": "black" },\n", - " { "id": 7, "label": "Heat Water", "color": "orange" },\n", - " { "id": 8, "label": "Place Grounds in Filter", "color": "orange" },\n", - " { "id": 9, "label": "Pour Water into Machine", "color": "orange" },\n", - " { "id": 10, "label": "Brew", "color": "orange" },\n", - " { "id": 11, "label": "Coffee", "color": "maroon" }\n", - " ],\n", - " "edges": [\n", - " { "source": 3, "target": 2, "label": "is ground by" },\n", - " { "source": 2, "target": 4, "label": "to" },\n", - " { "source": 4, "target": 5, "label": "place into" },\n", - " { "source": 5, "target": 6, "label": "insert into" },\n", - " { "source": 1, "target": 7, "label": "is used in" },\n", - " { "source": 7, "target": 6, "label": "pour into" },\n", - " { "source": 9, "target": 6, "label": "start" },\n", - " { "source": 6, "target": 10, "label": "to" },\n", - " { "source": 10, "target": 11, "label": "produce" }\n", - " ]\n", - " },\n", - " "comment": "Coffee Brewing Process"\n", - "}
Output:
<<Graphic displayed inline for the user to see>>
" + "
𝑓Ranvisualize_knowledge_graph...
Input:
{"kg": {"nodes": [{"id": 1, "label": "Cats", "color": "grey"}, {"id": 2, "label": "Feline", "color": "orange"}, {"id": 3, "label": "Whiskers", "color": "black"}, {"id": 4, "label": "Purring", "color": "purple"}, {"id": 5, "label": "Hunting", "color": "green"}, {"id": 6, "label": "Napping", "color": "yellow"}], "edges": [{"source": 1, "target": 2, "label": "is a"}, {"source": 1, "target": 3, "label": "has"}, {"source": 1, "target": 4, "label": "capable of"}, {"source": 1, "target": 5, "label": "engages in"}, {"source": 1, "target": 6, "label": "enjoys"}]}, "comment": "Knowledge Graph about Cats"}
" + ], + "text/plain": [ + "ToolArguments(display_id='c05a5fd0-c997-4ae3-af54-4bb009ec47ce', id='call_ElQGcyNI8iwxB0vd8qu8eQ5D', name='visualize_knowledge_graph', arguments='{\"kg\": {\"nodes\": [{\"id\": 1, \"label\": \"Cats\", \"color\": \"grey\"}, {\"id\": 2, \"label\": \"Feline\", \"color\": \"orange\"}, {\"id\": 3, \"label\": \"Whiskers\", \"color\": \"black\"}, {\"id\": 4, \"label\": \"Purring\", \"color\": \"purple\"}, {\"id\": 5, \"label\": \"Hunting\", \"color\": \"green\"}, {\"id\": 6, \"label\": \"Napping\", \"color\": \"yellow\"}], \"edges\": [{\"source\": 1, \"target\": 2, \"label\": \"is a\"}, {\"source\": 1, \"target\": 3, \"label\": \"has\"}, {\"source\": 1, \"target\": 4, \"label\": \"capable of\"}, {\"source\": 1, \"target\": 5, \"label\": \"engages in\"}, {\"source\": 1, \"target\": 6, \"label\": \"enjoys\"}]}, \"comment\": \"Knowledge Graph about Cats\"}', verbage='Ran', finished=True)" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
𝑓Ranvisualize_knowledge_graph...
Input:
{"kg": {"nodes": [{"id": 1, "label": "Dogs", "color": "brown"}, {"id": 2, "label": "Canine", "color": "orange"}, {"id": 3, "label": "Barking", "color": "black"}, {"id": 4, "label": "Tail Wagging", "color": "purple"}, {"id": 5, "label": "Fetching", "color": "green"}, {"id": 6, "label": "Loyalty", "color": "yellow"}], "edges": [{"source": 1, "target": 2, "label": "is a"}, {"source": 1, "target": 3, "label": "capable of"}, {"source": 1, "target": 4, "label": "shows"}, {"source": 1, "target": 5, "label": "enjoys"}, {"source": 1, "target": 6, "label": "known for"}]}, "comment": "Knowledge Graph about Dogs"}
" ], "text/plain": [ - "" + "ToolArguments(display_id='5a499b38-5089-40e1-b614-8a04380f04aa', id='call_csjm60iTTQH7R8TV0uE3KLF7', name='visualize_knowledge_graph', arguments='{\"kg\": {\"nodes\": [{\"id\": 1, \"label\": \"Dogs\", \"color\": \"brown\"}, {\"id\": 2, \"label\": \"Canine\", \"color\": \"orange\"}, {\"id\": 3, \"label\": \"Barking\", \"color\": \"black\"}, {\"id\": 4, \"label\": \"Tail Wagging\", \"color\": \"purple\"}, {\"id\": 5, \"label\": \"Fetching\", \"color\": \"green\"}, {\"id\": 6, \"label\": \"Loyalty\", \"color\": \"yellow\"}], \"edges\": [{\"source\": 1, \"target\": 2, \"label\": \"is a\"}, {\"source\": 1, \"target\": 3, \"label\": \"capable of\"}, {\"source\": 1, \"target\": 4, \"label\": \"shows\"}, {\"source\": 1, \"target\": 5, \"label\": \"enjoys\"}, {\"source\": 1, \"target\": 6, \"label\": \"known for\"}]}, \"comment\": \"Knowledge Graph about Dogs\"}', verbage='Ran', finished=True)" ] }, "metadata": {}, @@ -436,144 +398,180 @@ "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "1\n", - "\n", - "Water\n", - "\n", - "\n", - "\n", - "7\n", - "\n", - "Heat Water\n", - "\n", - "\n", - "\n", - "1->7\n", - "\n", - "\n", - "is used in\n", + "\n", + "Cats\n", "\n", "\n", "\n", "2\n", - "\n", - "Coffee Grinder\n", + "\n", + "Feline\n", "\n", - "\n", - "\n", - "4\n", - "\n", - "Grind Beans\n", - "\n", - "\n", - "\n", - "2->4\n", - "\n", - "\n", - "to\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "is a\n", "\n", "\n", "\n", "3\n", - "\n", - "Coffee Beans\n", + "\n", + "Whiskers\n", "\n", - "\n", - "\n", - "3->2\n", - "\n", - "\n", - "is ground by\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "has\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "Purring\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "capable of\n", "\n", "\n", "\n", "5\n", - "\n", - "Coffee Filter\n", + "\n", + "Hunting\n", "\n", - "\n", - "\n", - "4->5\n", - "\n", - "\n", - "place into\n", + "\n", + "\n", + "1->5\n", + "\n", + "\n", + "engages in\n", "\n", "\n", "\n", "6\n", - "\n", - "Coffee Machine\n", + "\n", + "Napping\n", "\n", - "\n", - "\n", - "5->6\n", - "\n", - "\n", - "insert into\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "enjoys\n", "\n", - "\n", - "\n", - "10\n", - "\n", - "Brew\n", "\n", - "\n", - "\n", - "6->10\n", - "\n", - "\n", - "to\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "Dogs\n", "\n", - "\n", - "\n", - "7->6\n", - "\n", - "\n", - "pour into\n", + "\n", + "\n", + "2\n", + "\n", + "Canine\n", "\n", - "\n", - "\n", - "8\n", - "\n", - "Place Grounds in Filter\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "is a\n", "\n", - "\n", - "\n", - "9\n", - "\n", - "Pour Water into Machine\n", + "\n", + "\n", + "3\n", + "\n", + "Barking\n", "\n", - "\n", - "\n", - "9->6\n", - "\n", - "\n", - "start\n", - "\n", - "\n", - "\n", - "11\n", - "\n", - "Coffee\n", - "\n", - "\n", - "\n", - "10->11\n", - "\n", - "\n", - "produce\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "capable of\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "Tail Wagging\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "shows\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "Fetching\n", + "\n", + "\n", + "\n", + "1->5\n", + "\n", + "\n", + "enjoys\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "Loyalty\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "known for\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -582,52 +580,36 @@ { "data": { "text/markdown": [ - "Here is the corrected visualization of the coffee brewing process:\n", + "Here are two knowledge graphs, one about cats and one about dogs:\n", + "\n", + "### Knowledge Graph about Cats\n", + "- **Cats:** Central to the graph, representing the domesticated feline.\n", + "- **Feline:** Cats are a type of feline, a family that includes other large and small wild cats.\n", + "- **Whiskers:** Cats have whiskers, which are sensitive tactile hairs that help them navigate and sense their environment.\n", + "- **Purring:** Cats are capable of purring, a behavior that can indicate contentment or comfort.\n", + "- **Hunting:** Cats engage in hunting, using their natural instincts to catch prey, even when they are not hungry.\n", + "- **Napping:** Cats enjoy napping and are known for sleeping for long periods throughout the day.\n", "\n", - "1. **Coffee Beans** are the starting material. They are whole, raw beans that need to be ground before brewing.\n", - "2. **Coffee Grinder** is used to grind the coffee beans into grounds.\n", - "3. **Grind Beans** is the process of breaking down coffee beans into smaller particles to increase the surface area for extraction.\n", - "4. **Coffee Filter** is used to hold the coffee grounds and prevent them from getting into the final brew.\n", - "5. **Coffee Machine** is the apparatus that will heat the water, pour it over the grounds, and brew the coffee.\n", - "6. **Water** is a crucial ingredient in coffee brewing.\n", - "7. **Heat Water** is the process to bring the water to the right temperature for brewing.\n", - "8. **Place Grounds in Filter** involves putting the ground coffee into the filter.\n", - "9. **Pour Water into Machine** is the action of adding water to the coffee machine for heating and brewing.\n", - "10. **Brew** is the actual process where hot water is passed through the grounds, extracting flavors and compounds to create coffee.\n", - "11. **Coffee** is the final product, a brewed beverage ready for consumption, represented here with the color maroon.\n", + "### Knowledge Graph about Dogs\n", + "- **Dogs:** Central to the graph, representing the domesticated canine.\n", + "- **Canine:** Dogs are a type of canine, a family that includes wolves, foxes, and other related species.\n", + "- **Barking:** Dogs are capable of barking, which they use for communication, alerting, and sometimes just for attention.\n", + "- **Tail Wagging:** Dogs show tail wagging as a sign of various emotions, including excitement, happiness, and nervousness.\n", + "- **Fetching:** Dogs enjoy fetching objects, which is a playful activity and can also be part of their training.\n", + "- **Loyalty:** Dogs are known for their loyalty to their human companions, often forming strong bonds.\n", "\n", - "The process flow is as follows: Coffee beans are ground by a coffee grinder, the grounds are placed into a coffee filter, which is then inserted into the coffee machine. Water is heated and poured into the machine, which then starts the brewing process, resulting in the production of coffee." + "These graphs highlight some of the key characteristics and behaviors associated with cats and dogs." ], "text/plain": [ - "Here is the corrected visualization of the coffee brewing process:\n", - "\n", - "1. **Coffee Beans** are the starting material. They are whole, raw beans that need to be ground before brewing.\n", - "2. **Coffee Grinder** is used to grind the coffee beans into grounds.\n", - "3. **Grind Beans** is the process of breaking down coffee beans into smaller particles to increase the surface area for extraction.\n", - "4. **Coffee Filter** is used to hold the coffee grounds and prevent them from getting into the final brew.\n", - "5. **Coffee Machine** is the apparatus that will heat the water, pour it over the grounds, and brew the coffee.\n", - "6. **Water** is a crucial ingredient in coffee brewing.\n", - "7. **Heat Water** is the process to bring the water to the right temperature for brewing.\n", - "8. **Place Grounds in Filter** involves putting the ground coffee into the filter.\n", - "9. **Pour Water into Machine** is the action of adding water to the coffee machine for heating and brewing.\n", - "10. **Brew** is the actual process where hot water is passed through the grounds, extracting flavors and compounds to create coffee.\n", - "11. **Coffee** is the final product, a brewed beverage ready for consumption, represented here with the color maroon.\n", - "\n", - "The process flow is as follows: Coffee beans are ground by a coffee grinder, the grounds are placed into a coffee filter, which is then inserted into the coffee machine. Water is heated and poured into the machine, which then starts the brewing process, resulting in the production of coffee." + "AssistantMessageView(display_id='8b576dbb-f0dc-4a4f-8428-237390227ca9', content='Here are two knowledge graphs, one about cats and one about dogs:\\n\\n### Knowledge Graph about Cats\\n- **Cats:** Central to the graph, representing the domesticated feline.\\n- **Feline:** Cats are a type of feline, a family that includes other large and small wild cats.\\n- **Whiskers:** Cats have whiskers, which are sensitive tactile hairs that help them navigate and sense their environment.\\n- **Purring:** Cats are capable of purring, a behavior that can indicate contentment or comfort.\\n- **Hunting:** Cats engage in hunting, using their natural instincts to catch prey, even when they are not hungry.\\n- **Napping:** Cats enjoy napping and are known for sleeping for long periods throughout the day.\\n\\n### Knowledge Graph about Dogs\\n- **Dogs:** Central to the graph, representing the domesticated canine.\\n- **Canine:** Dogs are a type of canine, a family that includes wolves, foxes, and other related species.\\n- **Barking:** Dogs are capable of barking, which they use for communication, alerting, and sometimes just for attention.\\n- **Tail Wagging:** Dogs show tail wagging as a sign of various emotions, including excitement, happiness, and nervousness.\\n- **Fetching:** Dogs enjoy fetching objects, which is a playful activity and can also be part of their training.\\n- **Loyalty:** Dogs are known for their loyalty to their human companions, often forming strong bonds.\\n\\nThese graphs highlight some of the key characteristics and behaviors associated with cats and dogs.', finished=False, has_displayed=True)" ] }, - "metadata": { - "text/markdown": { - "chatlab": { - "default": true - } - } - }, + "metadata": {}, "output_type": "display_data" } ], "source": [ - "await chat(\"A warning came up saying that darkbrown is not a known color.\")" + "await chat(\"Ok now make two knowledge graphs at the same time. One about cats and one about dogs.\")" ] } ], @@ -647,7 +629,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.1" + "version": "3.11.7" } }, "nbformat": 4,