Compare commits

...

28 Commits

Author SHA1 Message Date
Eugene Yurtsev a7545285c3 x 2023-12-08 11:36:16 -05:00
Eugene Yurtsev 295fb12f8c x 2023-12-08 11:36:02 -05:00
Eugene Yurtsev dd1f33ce02 x 2023-12-08 11:35:54 -05:00
Eugene Yurtsev 0ddb74036b q 2023-12-08 11:35:40 -05:00
Eugene Yurtsev cf10528b99 q 2023-12-08 11:35:35 -05:00
Eugene Yurtsev dbc254ebc0 x 2023-12-08 11:12:34 -05:00
Eugene Yurtsev 4c3de31e6f x 2023-12-08 10:52:12 -05:00
Eugene Yurtsev f8e5d32bc9 x 2023-12-08 10:51:57 -05:00
Eugene Yurtsev 40250950db x 2023-12-08 10:51:52 -05:00
Eugene Yurtsev 9adc5b96f5 x 2023-12-07 23:10:22 -05:00
Eugene Yurtsev 85c8d29342 x 2023-12-07 23:01:56 -05:00
Eugene Yurtsev a42c70f315 x 2023-12-07 22:54:29 -05:00
Eugene Yurtsev 9c6ec01219 x 2023-12-07 22:54:23 -05:00
Eugene Yurtsev d2defc95e3 x 2023-12-07 22:54:14 -05:00
Eugene Yurtsev 8b0e52fdc4 x 2023-12-07 22:54:09 -05:00
Eugene Yurtsev 22b90df0ad x 2023-12-07 22:36:33 -05:00
Eugene Yurtsev 8ba4340730 x 2023-12-07 22:27:08 -05:00
Eugene Yurtsev 2f0b4e9bed x 2023-12-07 22:27:01 -05:00
Eugene Yurtsev 910bd60832 x 2023-12-07 22:23:36 -05:00
Eugene Yurtsev 5b4672a33b x 2023-12-07 18:21:22 -05:00
Eugene Yurtsev 9f827eaca5 Update README.md (#108) 2023-12-07 13:38:25 -05:00
Eugene Yurtsev d9fc08b05c Update README.md (#107) 2023-12-07 13:34:55 -05:00
Lance Martin 8a5ba6d575 Minor cleanup to multi-modal embeddings docs (#105) 2023-12-05 13:40:42 -08:00
William FH 8204930f2b 0.0.7 (#104)
Adds the multimodal benchmark.
2023-12-05 13:14:44 -08:00
Lance Martin 013fe6a153 Multi modal RAG benchmark (#101)
* Example notebooks for eval of multi-modal RAG w/ mm-embd and
mv-retriever vs baseline top-k RAG

---------

Co-authored-by: William Fu-Hinthorn <13333726+hinthornw@users.noreply.github.com>
2023-12-05 12:04:01 -08:00
William FH 01ffffd04c Update Chat Extraction Notebook (#102) 2023-12-03 18:28:51 -08:00
William FH 4ddbbc0ff8 Add Archived (#53)
Moves the other tasks to the archive
2023-12-01 10:56:44 -08:00
William FH 5ffdbb5c4c Add chat categorization dataset (#98)
This task is meant to test a couple things:
1. Classification -> both on common things where it is expected to
perform well (e.g., sentiment, toxicity -> which currently is always 0)
2. Structured json output -> the schema is nested, which confused some
of the smaller 7b models i tested out but works fine for llama 32b code
instruct (and OAI/anthropic)


Includes a couple common things like enums.
2023-12-01 10:17:50 -08:00
124 changed files with 7290 additions and 247 deletions
+2 -4
View File
@@ -1,6 +1,4 @@
🚧 Under Active Development 🚧
# 🦜💪 LangChain Benchmarks
# 🦜💯 LangChain Benchmarks
[![Release Notes](https://img.shields.io/github/release/langchain-ai/langchain-benchmarks)](https://github.com/langchain-ai/langchain-benchmarks/releases)
[![CI](https://github.com/langchain-ai/langchain-benchmarks/actions/workflows/ci.yml/badge.svg)](https://github.com/langchain-ai/langchain-benchmarks/actions/workflows/ci.yml)
@@ -35,7 +33,7 @@ pip install -U langchain-benchmarks
All the benchmarks come with an associated benchmark dataset stored in [LangSmith](https://smith.langchain.com). To take advantage of the eval and debugging experience, [sign up](https://smith.langchain.com), and set your API key in your environment:
```bash
export LANGCHAIN_API_KEY=sk-...
export LANGCHAIN_API_KEY=ls-...
```
## Repo Structure
+52
View File
@@ -0,0 +1,52 @@
import inspect
from textwrap import dedent
from typing import List
from langchain.tools.base import StructuredTool
from agents.encoder import FunctionDefinition, Parameter
# This is temporary until we have a better way to represent parameters
def get_parameters_from_tool(tool: StructuredTool) -> List[Parameter]:
"""Convert a langchain tool to a tool user tool."""
schema = tool.args_schema.schema()
properties = schema["properties"]
parameters = []
# Is this needed or is string OK?
type_adapter = {
"string": "str", # str or string?
"integer": "int",
"number": "float",
"boolean": "bool",
}
for key, value in properties.items():
parameters.append(
{
"name": key,
"type": type_adapter.get(value["type"], value["type"]),
"description": value.get("description", ""),
}
)
return parameters
#
def convert_tool_to_function_definition(tool: StructuredTool) -> FunctionDefinition:
"""Convert a langchain tool to a tool user tool."""
# Here we re-inspect the underlying function to get the doc-string
# since StructuredTool modifies it, but we want the raw one for maximum
# flexibility.
description = inspect.getdoc(tool.func)
parameters = get_parameters_from_tool(tool)
return {
"name": tool.name,
"description": dedent(description),
"parameters": parameters,
"return_value": {
"type": "Any",
},
}
+105
View File
@@ -0,0 +1,105 @@
from typing import List, Literal, Sequence, Tuple, Union
from langchain.agents import AgentOutputParser
from langchain.prompts.chat import ChatPromptTemplate
from langchain.schema.messages import HumanMessage
from langchain.schema.runnable import Runnable
from langchain.tools import StructuredTool
from langchain_core.agents import AgentAction, AgentFinish
from langchain_core.language_models import BaseChatModel, BaseLanguageModel
from langchain_core.messages import BaseMessage
from langchain_core.prompts import MessagesPlaceholder
from typing_extensions import NotRequired, TypedDict
from agents.adapters import convert_tool_to_function_definition
from agents.encoder import AstPrinter, TypeScriptEncoder
from agents.prompts import AGENT_INSTRUCTIONS_BLOB_STYLE
def format_observation(tool_name: str, observation: str) -> BaseMessage:
"""Format the observation."""
result = (
"<tool_output>\n"
f"<tool_name>{tool_name}</tool_name>\n"
f"<output>{observation}</output>\n"
"</tool_output>"
)
return HumanMessage(content=result)
def format_steps_for_chat(
intermediate_steps: List[Tuple[AgentAction, str]]
) -> List[BaseMessage]:
"""Format the steps."""
messages = []
for action, observation in intermediate_steps:
if not isinstance(action, AgentAction):
if action.tool != "_Exception":
raise AssertionError(f"Unexpected step: {action}. type: {type(action)}")
messages.append(HumanMessage(content=observation))
messages.extend(action.messages)
messages.append(format_observation(action.tool, observation))
return messages
# PUBLIC API
class AgentInput(TypedDict):
"""The input to the agent."""
input: str
"""The input to the agent."""
intermediate_steps: List[Tuple[AgentAction, str]]
"""The intermediate steps taken by the agent."""
examples: NotRequired[List[BaseMessage]]
"""A list of messages that can be used to form example traces."""
def create_agent(
model: Union[BaseChatModel, BaseLanguageModel],
tools: Sequence[StructuredTool],
parser: AgentOutputParser,
*,
ast_printer: Union[AstPrinter, Literal["xml"]] = "xml",
) -> Runnable[AgentInput, Union[AgentAction, AgentFinish]]:
"""Create an agent for a chat model."""
if isinstance(ast_printer, str):
if ast_printer == "xml":
ast_printer = AstPrinter()
elif ast_printer == "typescript":
ast_printer = TypeScriptEncoder()
else:
raise ValueError(f"Unknown ast printer: {ast_printer}")
elif isinstance(ast_printer, AstPrinter):
pass
else:
raise TypeError(
f"Expected AstPrinter or str, got {type(ast_printer)} for `ast_printer`"
)
function_definitions = [convert_tool_to_function_definition(tool) for tool in tools]
tool_description = ast_printer.visit_function_definitions(function_definitions)
template = ChatPromptTemplate.from_messages(
[
("system", AGENT_INSTRUCTIONS_BLOB_STYLE),
MessagesPlaceholder("examples"), # Can use to add example traces
("human", "{input}"),
MessagesPlaceholder("history"),
]
).partial(tool_description=tool_description)
agent = (
{
"input": lambda x: x["input"],
"history": lambda x: format_steps_for_chat(x["intermediate_steps"]),
"examples": lambda x: x.get("examples", []),
}
| template
| model.bind(stop=["</tool>"])
| parser
)
return agent
+226
View File
@@ -0,0 +1,226 @@
"""Prototyping code for rendering function definitions, invocations, and results.
Types are simplified for now to `str`.
We should actually support something like pydantic or jsonschema for the types, so
we can expand them recursively for nested types.
"""
import abc
from typing import Any, List, Optional
from typing_extensions import NotRequired, TypedDict
class Parameter(TypedDict):
"""Representation for a parameter."""
name: str
type: str
description: str
class Arguments(TypedDict):
"""Arguments are passed to a function during function invocation."""
name: Optional[str]
value: Any
class ReturnValue(TypedDict):
"""Representation for a return value of a function call."""
type: str
description: NotRequired[str]
class FunctionDefinition(TypedDict):
"""Representation for a function."""
name: str
description: str # Function description
parameters: List[Parameter]
return_value: ReturnValue
class FunctionInvocation(TypedDict):
"""Representation for a function invocation."""
id: NotRequired[str]
name: str
arguments: List[Arguments]
class FunctionResult(TypedDict):
"""Representation for a function result."""
id: NotRequired[str]
name: str
result: Optional[str]
error: Optional[str]
class Visitor(abc.ABC):
def visit_function_definition(self, function_definition: FunctionDefinition) -> str:
"""Render a function."""
raise NotImplementedError()
def visit_function_definitions(
self, function_definitions: List[FunctionDefinition]
) -> str:
"""Render a function."""
raise NotImplementedError()
def visit_function_invocation(self, function_invocation: FunctionInvocation) -> str:
"""Render a function invocation."""
raise NotImplementedError()
def visit_function_result(self, function_result: FunctionResult) -> str:
"""Render a function result."""
raise NotImplementedError()
class AstPrinter(Visitor):
"""Print the AST."""
class XMLEncoder(AstPrinter):
def visit_function_definition(self, function_definition: FunctionDefinition) -> str:
"""Render a function."""
parameters_as_strings = [
"<parameter>\n"
f"<name>{parameter['name']}</name>\n"
f"<type>{parameter['type']}</type>\n"
f"<description>{parameter['description']}</description>\n"
"</parameter>\n"
for parameter in function_definition["parameters"]
]
function = (
"<function>\n"
f"<function_name>{function_definition['name']}</function_name>\n"
"<description>\n"
f"{function_definition['description']}\n"
"</description>\n"
"<parameters>\n"
f"{''.join(parameters_as_strings)}" # Already includes trailing newline
"</parameters>\n"
"<return_value>\n"
f"<type>{function_definition['return_value']['type']}</type>\n"
f"<description>{function_definition['return_value']['description']}</description>\n"
"</return_value>\n"
"</function>"
)
return function
def visit_function_definitions(
self, function_definitions: List[FunctionDefinition]
) -> str:
"""Render a function."""
strs = [
self.visit_function_definition(function_definition)
for function_definition in function_definitions
]
return "<functions>\n" + "\n".join(strs) + "\n</functions>"
def visit_function_invocation(self, invocation: FunctionInvocation) -> str:
"""Render a function invocation."""
arguments_as_strings = [
"<argument>\n"
f"<name>{argument['name']}</name>\n"
f"<value>{argument['value']}</value>\n"
"</argument>\n"
for argument in invocation["arguments"]
]
lines = ["<function_invocation>"]
if invocation.get("id"):
lines.append(f"<id>{invocation['id']}</id>")
lines.extend(
[
f"<function_name>{invocation['name']}</function_name>\n"
"<arguments>\n"
f"{''.join(arguments_as_strings)}" # Already includes trailing newline
"</arguments>\n"
"</function_invocation>"
]
)
return "\n".join(lines)
def visit_function_result(self, function_result: FunctionResult) -> str:
"""Render a function result."""
lines = [
"<function_result>",
]
if function_result.get("id"):
lines.append(f"<id>{function_result['id']}</id>")
lines.extend(
[
f"<function_name>{function_result['name']}</function_name>",
f"<result>{function_result['result']}</result>",
f"<error>{function_result['error']}</error>",
"</function_result>",
]
)
return "\n".join(lines)
class TypeScriptEncoder(AstPrinter):
def visit_function_definition(self, function_definition: FunctionDefinition) -> str:
"""Render a function."""
parameters_as_strings = [
f"{parameter['name']}: {parameter['type']}"
for parameter in function_definition["parameters"]
]
# Let's use JSdoc style comments
# First the function description
lines = [
f"// {function_definition['description']}",
# Then the parameter descriptions
*[
f"// @param {parameter['name']} {parameter['description']}"
for parameter in function_definition["parameters"]
],
# Then the return value description
f"// @returns {function_definition['return_value']['description']}",
# Then the function definition
f"function {function_definition['name']}("
f"{', '.join(parameters_as_strings)}): "
f"{function_definition['return_value']['type']};",
]
# finally join
function = "\n".join(lines)
return function
def visit_function_definitions(
self, function_definitions: List[FunctionDefinition]
) -> str:
"""Render a function."""
strs = [
self.visit_function_definition(function_definition)
for function_definition in function_definitions
]
return "\n\n".join(strs)
def visit_function_invocation(self, invocation: FunctionInvocation) -> str:
"""Render a function invocation."""
arguments_as_strings = [
f"{argument['name']}: {argument['value']}"
for argument in invocation["arguments"]
]
lines = [f"{invocation['name']}(" f"{', '.join(arguments_as_strings)});"]
return "\n".join(lines)
def visit_function_result(self, function_result: FunctionResult) -> str:
"""Render a function result."""
lines = []
if function_result["error"]:
lines.append(f"ERROR: {function_result['error']}")
else:
lines.append(f"> {function_result['result']}")
if function_result.get("id"):
lines.append(f"// ID: {function_result['id']}")
return "\n".join(lines)
+25
View File
@@ -0,0 +1,25 @@
# EXAMPLE_TRACE = [
# HumanMessage(content="type the letter 'o'"),
# AIMessage(
# content="""
# <tool>
# {
# "tool_name": "type_letter",
# "arguments": {
# "letter": "o"
# }
# }
# </tool>\
# """
# ),
# HumanMessage(
# content="""\
# <tool_outputs>
# <tool_name>type_letter</tool_name>
# <output>o</output>
# </tool_outputs>\
# """
# ),
# ]
#
#
+75
View File
@@ -0,0 +1,75 @@
from typing import List, Optional
from langchain.agents import AgentExecutor
from langchain.chat_models import ChatAnthropic, ChatFireworks
from langchain_core.runnables import Runnable, RunnableConfig
from agents.agent import create_agent
from agents.parser import ParameterizedAgentParser
from langchain_benchmarks.model_registration import FIREWORK_NAME_TO_MODEL
from langchain_benchmarks.schema import ToolUsageTask
from langchain_benchmarks.tool_usage import apply_agent_executor_adapter
class CustomAgentFactory:
def __init__(self, task: ToolUsageTask, model: str) -> None:
"""Create an OpenAI agent factory for the given task.
Args:
task: The task to create an agent factory for.
"""
if model not in self.list_models():
raise ValueError(f"Unknown model: {model}")
self.task = task
self.model = model
@staticmethod
def list_models() -> List[str]:
"""List all models."""
return sorted(
[
"claude-2.1",
"claude-2",
*FIREWORK_NAME_TO_MODEL.keys(),
]
)
def __call__(self) -> Runnable:
env = self.task.create_environment()
if self.model in {"claude-2.1", "claude-2"}:
model = ChatAnthropic(model=self.model, temperature=0)
elif self.model in FIREWORK_NAME_TO_MODEL:
model = ChatFireworks(
model=FIREWORK_NAME_TO_MODEL[self.model], temperature=0
)
else:
raise ValueError(f"Unknown model: {self.model}")
def _add_task_instructions(
input: dict, config: Optional[RunnableConfig] = None, **kwargs
) -> dict:
"""Add task instructions to the question."""
input = input.copy()
input["question"] = (
f"{self.task.instructions}\nWrite down your answer, "
f"but do not explain it. Input: `{input['question']}`"
)
return input
agent = create_agent(
model,
env.tools,
ParameterizedAgentParser(
wrapping_xml_tag="tool", require_closing_xml_tag=False
),
)
executor = AgentExecutor(
agent=agent,
tools=env.tools,
handle_parsing_errors=True,
return_intermediate_steps=True,
)
return _add_task_instructions | apply_agent_executor_adapter(
executor, state_reader=env.read_state
)
+120
View File
@@ -0,0 +1,120 @@
import ast
import re
from typing import Any, Union
from langchain.agents import AgentOutputParser
from langchain.pydantic_v1 import BaseModel, Field, ValidationError
from langchain_core.agents import AgentAction, AgentActionMessageLog, AgentFinish
from langchain_core.exceptions import OutputParserException
from langchain_core.messages import AIMessage
class _ToolInvocationRequest(BaseModel):
"""Light-weight pydantic model for validating the raw tool invocation request.
The purpose of this model, is to make sure that whatever as parsed from
the raw llm output has `tool_name` and potential `arguments` fields, and
nothing else.
"""
tool_name: str
# OK parameterless tools which do not take arguments
named_arguments: Any = Field(default_factory=dict)
class ParameterizedAgentParser(AgentOutputParser):
"""A generalized parser that makes it easier to parameterize different parsing."""
wrapping_xml_tag: str
"""The tag that wraps the function invocation request.
For example, if "tool", then the function invocation request should be wrapped
in <tool>...</tool>.
"""
require_closing_xml_tag: bool = False
"""Whether we should require a closing tag for the wrapping_xml_tag.
For example, if True, then the function invocation request should be wrapped
"""
def parse(self, text: str) -> Union[AgentFinish, AgentAction]:
"""Parse the output of the agent."""
open_tag = f"<{self.wrapping_xml_tag}>"
close_tag = f"</{self.wrapping_xml_tag}>"
if open_tag in text:
# This is a hack to make sure that </tool> is always present
# in the output if <tool>. </tool> may be a stop sequence for the
# language model, so depending on implementation
# the stop sequence may be cut off.
# There might be a better way to do this, but this works and
# is simple.
if not self.require_closing_xml_tag:
text += close_tag
pattern = rf"{open_tag}(?P<invocation>.*?){close_tag}"
match = re.search(pattern, text, re.DOTALL)
if match:
content = match.group("invocation").strip()
return parse_invocation(content, self.wrapping_xml_tag)
return AgentFinish(
log=text,
return_values={
"output": text,
},
)
def parse_invocation(text: str, tag: str) -> AgentAction:
"""Parse the content of the function invocation.
Args:
text: The text to parse.
tag: The tag that wraps the function invocation request.
Returns:
An AgentAction that corresponds to the function invocation.
Raises:
OutputParserException: If the parsing fails.
This exception is meant to be caught by the agent executor and
handled appropriately to provide feedback to the LLM.
"""
ai_content = f"<{tag}>{text}</{tag}>"
try:
result = ast.literal_eval(text)
except Exception as e:
# Convert this to something controllable by the user.
err_msg = (
f"ERROR: Please use the format "
f'<{tag}>{{"tool_name": $TOOL_NAME, "arguments": $ARGUMENTS}}</{tag}>'
)
raise OutputParserException(
error=e,
llm_output=ai_content,
observation=err_msg,
send_to_llm=True,
)
try:
request = _ToolInvocationRequest(**result)
except ValidationError as e:
err_msg = (
f"ERROR: Please use the format "
f'<{tag}>{{"tool_name": $TOOL_NAME, "arguments": $ARGUMENTS}}</{tag}>'
)
raise OutputParserException(
error=e,
llm_output=ai_content,
send_to_llm=True,
observation=err_msg,
)
return AgentActionMessageLog(
message_log=[AIMessage(content=ai_content)],
tool=request.tool_name,
tool_input=request.named_arguments,
log=f"\nInvoking {request.tool_name}: {request.named_arguments}\n\t",
)
+42
View File
@@ -0,0 +1,42 @@
AGENT_INSTRUCTIONS_XML_FORMAT = """\
In this environment you have access to a set of tools you can use to answer the user's question.
You may call them like this:
<function_calls>
<invoke>
<tool_name>$TOOL_NAME</tool_name>
<parameters>
<$PARAMETER_NAME>$PARAMETER_VALUE</$PARAMETER_NAME>
...
</parameters>
</invoke>
</function_calls>
Here are the tools available:
{tool_description}
""" # noqa: E501
AGENT_INSTRUCTIONS_BLOB_STYLE = """\
In this environment you have access to a set of tools you can use to answer the user's question.
Here are the tools available:
{tool_description}
You may call one tool at a time using a format that includes <tool> and </tool> tag.
Inside the tag the content is a python dictionary that uses python literals (e.g., numbers, strings, lists, dictionaries, etc.) to specify the tool invocation.
It must match the schema of the function as described in the tool description.
"arguments" is a dictionary of the arguments to the function.
<tool>
{{
"tool_name": $TOOL_NAME,
"arguments": $ARGUMENTS
}}
</tool>
If you do not know the answer use more tools. You can only take a single action at a time.\
""" # noqa: E501
@@ -0,0 +1,29 @@
"""Test XML encoding and decoding of function definitions, invocation, and results."""
from agents.encoder import (
FunctionDefinition,
TypeScriptEncoder,
)
def test_function_definition() -> None:
"""Test encoding a function definition."""
function_definition = FunctionDefinition(
name="test_function",
description="A test function",
parameters=[
{"name": "test_parameter", "type": "str", "description": "A test parameter"}
],
return_value={"type": "str", "description": "A test return value"},
)
encoder = TypeScriptEncoder()
xml = encoder.visit_function_definition(function_definition)
assert xml == (
"// A test function\n"
"// @param test_parameter A test parameter\n"
"// @returns A test return value\n"
"function test_function(test_parameter: str): str;"
)
# Not important to test other ones right now since we can't parse / interpret
# typescript anyway.
+79
View File
@@ -0,0 +1,79 @@
"""Test XML encoding and decoding of function definitions, invocation, and results."""
from agents.encoder import (
FunctionDefinition,
FunctionInvocation,
FunctionResult,
XMLEncoder,
)
def test_function_definition_encoding() -> None:
"""Test encoding a function definition."""
function_definition = FunctionDefinition(
name="test_function",
description="A test function",
parameters=[
{"name": "test_parameter", "type": "str", "description": "A test parameter"}
],
return_value={"type": "str", "description": "A test return value"},
)
encoder = XMLEncoder()
xml = encoder.visit_function_definition(function_definition)
assert xml == (
"<function>\n"
"<function_name>test_function</function_name>\n"
"<description>\n"
"A test function\n"
"</description>\n"
"<parameters>\n"
"<parameter>\n"
"<name>test_parameter</name>\n"
"<type>str</type>\n"
"<description>A test parameter</description>\n"
"</parameter>\n"
"</parameters>\n"
"<return_value>\n"
"<type>str</type>\n"
"<description>A test return value</description>\n"
"</return_value>\n"
"</function>"
)
def test_function_result_encoding() -> None:
"""Test encoding a function result."""
function_result = FunctionResult(
name="test_function",
result="test_result",
error="test_error",
)
encoder = XMLEncoder()
xml = encoder.visit_function_result(function_result)
assert xml == (
"<function_result>\n"
"<function_name>test_function</function_name>\n"
"<result>test_result</result>\n"
"<error>test_error</error>\n"
"</function_result>"
)
def test_function_invocation() -> None:
"""Test function invocation."""
function_invocation = FunctionInvocation(
name="test_function",
arguments=[{"name": "test_argument", "value": "test_value"}],
)
encoder = XMLEncoder()
xml = encoder.visit_function_invocation(function_invocation)
assert xml == (
"<function_invocation>\n"
"<function_name>test_function</function_name>\n"
"<arguments>\n"
"<argument>\n"
"<name>test_argument</name>\n"
"<value>test_value</value>\n"
"</argument>\n"
"</arguments>\n"
"</function_invocation>"
)
+58
View File
@@ -0,0 +1,58 @@
import pytest
from langchain.tools import tool
from agents.adapters import convert_tool_to_function_definition
from agents.encoder import XMLEncoder
@tool
def get_hello() -> str:
"""Get hello."""
return "hello"
@tool
def repeat(x: str) -> str:
"""Repeat x.
Args:
x: The string to repeat.
Returns:
The repeated string.
"""
return x
def test_parameterless_function() -> None:
"""Test foo."""
function_definition = convert_tool_to_function_definition(get_hello)
assert function_definition == {
"name": "get_hello",
"description": "Get hello.",
"parameters": [],
"return_value": {
"type": "Any",
},
}
@pytest.mark.skip("Need to fix handling of leading whitespace")
def test_function_with_parameters() -> None:
import textwrap
doc = textwrap.dedent(repeat.func.__doc__)
assert convert_tool_to_function_definition(repeat) == {
"name": "repeat",
"description": doc,
"parameters": [
{
"name": "x",
"type": "str",
"description": "", # Need to fix this
}
],
"return_value": {
"type": "Any",
},
}
+11
View File
@@ -0,0 +1,11 @@
from langchain_adapters.alternative import AgentOutputParser
from langchain_core.agents import AgentFinish
def test_parser() -> None:
"""Test parser."""
parser = AgentOutputParser(require_closing_tag=False, tag="tool")
assert isinstance(parser.invoke("goodbye"), AgentFinish)
assert parser.invoke("<tool>hello</tool>") == "hello"
assert parser.invoke("<tool>hello") == "hello"
# assert isinstance(parser.invoke("<tag>hello</tag>"), AgentAction)
+35
View File
@@ -0,0 +1,35 @@
"""Throttle using a token bucket."""
import threading
import time
class Throttle:
def __init__(self, rate: int) -> None:
"""Initialize the throttle."""
self.rate = rate
self.tokens = 0
self._consume_lock = threading.Lock()
self.last = None
def consume(self, amount: int = 0) -> int:
"""Consume the given amount of tokens."""
with self._consume_lock:
now = time.time()
# initialize on first call to avoid a burst
if self.last is None:
self.last = now
elapsed = now - self.last
if elapsed * self.rate > 1:
self.tokens += elapsed * self.rate
self.last = now
self.tokens = min(self.tokens, self.rate)
if self.tokens >= amount:
self.tokens -= amount
return amount
return 0

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Some files were not shown because too many files have changed in this diff Show More