chatterer 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ from .code_agent import (
2
+ CodeExecutionResult,
3
+ FunctionSignature,
4
+ get_default_repl_tool,
5
+ insert_callables_into_global,
6
+ )
7
+ from .image import Base64Image
8
+
9
+ __all__ = [
10
+ "Base64Image",
11
+ "FunctionSignature",
12
+ "CodeExecutionResult",
13
+ "get_default_repl_tool",
14
+ "insert_callables_into_global",
15
+ ]
@@ -0,0 +1,134 @@
1
+ import inspect
2
+ import textwrap
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Callable,
6
+ Iterable,
7
+ NamedTuple,
8
+ Optional,
9
+ Self,
10
+ )
11
+
12
+ from langchain_core.runnables.config import RunnableConfig
13
+
14
+ if TYPE_CHECKING:
15
+ from langchain_experimental.tools import PythonAstREPLTool
16
+
17
+
18
+ class FunctionSignature(NamedTuple):
19
+ name: str
20
+ callable: Callable[..., object]
21
+ signature: str
22
+
23
+ @classmethod
24
+ def from_callable(cls, callable: Callable[..., object]) -> Self:
25
+ """
26
+ Get the name and signature of a function as a string.
27
+ """
28
+ # Determine if the function is async
29
+ is_async_func = inspect.iscoroutinefunction(callable)
30
+ function_def = "async def" if is_async_func else "def"
31
+
32
+ # Determine the function name based on the type of callable
33
+ if inspect.isfunction(callable):
34
+ # For regular Python functions, use __code__.co_name
35
+ function_name = callable.__code__.co_name
36
+ elif hasattr(callable, "name"):
37
+ # For StructuredTool or similar objects with a 'name' attribute
38
+ function_name = callable.name # type: ignore
39
+ elif hasattr(callable, "__name__"):
40
+ # For other callables with a __name__ attribute
41
+ function_name = callable.__name__
42
+ else:
43
+ # Fallback to the class name if no name is found
44
+ function_name = type(callable).__name__
45
+
46
+ # Build the signature string
47
+ signature = f"{function_def} {function_name}{inspect.signature(callable)}:"
48
+ docstring = inspect.getdoc(callable)
49
+ if docstring:
50
+ docstring = f'"""{docstring.strip()}"""'
51
+ return cls(
52
+ name=function_name, callable=callable, signature=f"{signature}\n{textwrap.indent(docstring, ' ')}"
53
+ )
54
+ else:
55
+ return cls(name=function_name, callable=callable, signature=signature)
56
+
57
+ @classmethod
58
+ def from_callables(cls, callables: Iterable[Callable[..., object]]) -> list[Self]:
59
+ return [cls.from_callable(callable) for callable in callables]
60
+
61
+ @classmethod
62
+ def as_prompt(
63
+ cls,
64
+ callables: Iterable[Self],
65
+ prefix: Optional[str] = "You can use the pre-made functions below without defining them:\n",
66
+ sep: str = "\n---\n",
67
+ ) -> str:
68
+ """
69
+ Generate a prompt string from a list of callables.
70
+ """
71
+ body: str = sep.join(fsig.signature for fsig in callables)
72
+ if prefix:
73
+ return f"{prefix}{body}"
74
+ return body
75
+
76
+
77
+ class CodeExecutionResult(NamedTuple):
78
+ code: str
79
+ output: str
80
+
81
+ @classmethod
82
+ def from_code(
83
+ cls,
84
+ code: str,
85
+ repl_tool: Optional["PythonAstREPLTool"] = None,
86
+ config: Optional[RunnableConfig] = None,
87
+ function_signatures: Optional[Iterable[FunctionSignature]] = None,
88
+ **kwargs: object,
89
+ ) -> Self:
90
+ """
91
+ Execute code using the Python Code Execution Language Model.
92
+ """
93
+ if repl_tool is None:
94
+ repl_tool = get_default_repl_tool()
95
+ if function_signatures is not None:
96
+ insert_callables_into_global(function_signatures=function_signatures, repl_tool=repl_tool)
97
+ output = str(repl_tool.invoke(code, config=config, **kwargs)) # pyright: ignore[reportUnknownMemberType]
98
+ return cls(code=code, output=output)
99
+
100
+ @classmethod
101
+ async def afrom_code(
102
+ cls,
103
+ code: str,
104
+ repl_tool: Optional["PythonAstREPLTool"] = None,
105
+ config: Optional[RunnableConfig] = None,
106
+ function_signatures: Optional[Iterable[FunctionSignature]] = None,
107
+ **kwargs: object,
108
+ ) -> Self:
109
+ """
110
+ Execute code using the Python Code Execution Language Model asynchronously.
111
+ """
112
+ if repl_tool is None:
113
+ repl_tool = get_default_repl_tool()
114
+ if function_signatures is not None:
115
+ insert_callables_into_global(function_signatures=function_signatures, repl_tool=repl_tool)
116
+ output = str(await repl_tool.ainvoke(code, config=config, **kwargs)) # pyright: ignore[reportUnknownMemberType]
117
+ return cls(code=code, output=output)
118
+
119
+
120
+ def get_default_repl_tool() -> "PythonAstREPLTool":
121
+ from langchain_experimental.tools import PythonAstREPLTool
122
+
123
+ return PythonAstREPLTool()
124
+
125
+
126
+ def insert_callables_into_global(
127
+ function_signatures: Iterable[FunctionSignature], repl_tool: "PythonAstREPLTool"
128
+ ) -> None:
129
+ """Insert callables into the REPL tool's globals."""
130
+ repl_globals: Optional[dict[str, object]] = repl_tool.globals # pyright: ignore[reportUnknownMemberType]
131
+ if repl_globals is None:
132
+ repl_tool.globals = {fsig.name: fsig.callable for fsig in function_signatures}
133
+ else:
134
+ repl_globals.update({fsig.name: fsig.callable for fsig in function_signatures})