grasp_agents 0.2.6__tar.gz → 0.2.8__tar.gz
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.
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/PKG-INFO +1 -1
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/pyproject.toml +1 -1
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/cloud_llm.py +3 -6
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/llm.py +17 -5
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/message_converters.py +1 -1
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/utils.py +36 -70
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/.gitignore +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/LICENSE.md +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/README.md +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/__init__.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/agent_message.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/agent_message_pool.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/base_agent.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/comm_agent.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/costs_dict.yaml +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/generics_utils.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/grasp_logging.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/http_client.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/llm_agent.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/llm_agent_state.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/memory.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/__init__.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/completion_converters.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/content_converters.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/converters.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/openai_llm.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/openai/tool_converters.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/printer.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/prompt_builder.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/rate_limiting/__init__.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/rate_limiting/rate_limiter_chunked.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/rate_limiting/types.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/rate_limiting/utils.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/run_context.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/tool_orchestrator.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/__init__.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/completion.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/content.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/converters.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/io.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/message.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/typing/tool.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/usage_tracker.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/workflow/__init__.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/workflow/looped_agent.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/workflow/sequential_agent.py +0 -0
- {grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/workflow/workflow_agent.py +0 -0
@@ -2,12 +2,12 @@ import fnmatch
|
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
from abc import abstractmethod
|
5
|
-
from collections.abc import AsyncIterator, Sequence
|
5
|
+
from collections.abc import AsyncIterator, Mapping, Sequence
|
6
6
|
from copy import deepcopy
|
7
7
|
from typing import Any, Generic, Literal
|
8
8
|
|
9
9
|
import httpx
|
10
|
-
from pydantic import BaseModel
|
10
|
+
from pydantic import BaseModel
|
11
11
|
from tenacity import (
|
12
12
|
RetryCallState,
|
13
13
|
retry,
|
@@ -104,7 +104,7 @@ class CloudLLM(LLM[SettingsT, ConvertT], Generic[SettingsT, ConvertT]):
|
|
104
104
|
llm_settings: SettingsT | None = None,
|
105
105
|
model_id: str | None = None,
|
106
106
|
tools: list[BaseTool[BaseModel, Any, Any]] | None = None,
|
107
|
-
response_format: type | None = None,
|
107
|
+
response_format: type | Mapping[str, type] | None = None,
|
108
108
|
# Connection settings
|
109
109
|
async_http_client_params: (
|
110
110
|
dict[str, Any] | AsyncHTTPClientParams | None
|
@@ -148,9 +148,6 @@ class CloudLLM(LLM[SettingsT, ConvertT], Generic[SettingsT, ConvertT]):
|
|
148
148
|
fnmatch.fnmatch(self._model_name, pat)
|
149
149
|
for pat in PROVIDERS[api_provider]["struct_output_support"]
|
150
150
|
)
|
151
|
-
self._response_format_pyd: TypeAdapter[Any] | None = (
|
152
|
-
TypeAdapter(self._response_format) if response_format else None
|
153
|
-
)
|
154
151
|
if (
|
155
152
|
self._llm_settings.get("use_structured_outputs")
|
156
153
|
and not self._struct_output_support
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import logging
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
-
from collections.abc import AsyncIterator, Sequence
|
3
|
+
from collections.abc import AsyncIterator, Mapping, Sequence
|
4
4
|
from typing import Any, Generic, TypeVar, cast
|
5
5
|
from uuid import uuid4
|
6
6
|
|
7
|
-
from pydantic import BaseModel
|
7
|
+
from pydantic import BaseModel, TypeAdapter
|
8
8
|
from typing_extensions import TypedDict
|
9
9
|
|
10
10
|
from .memory import MessageHistory
|
@@ -33,7 +33,7 @@ class LLM(ABC, Generic[SettingsT, ConvertT]):
|
|
33
33
|
model_id: str | None = None,
|
34
34
|
llm_settings: SettingsT | None = None,
|
35
35
|
tools: list[BaseTool[BaseModel, Any, Any]] | None = None,
|
36
|
-
response_format: type | None = None,
|
36
|
+
response_format: type | Mapping[str, type] | None = None,
|
37
37
|
**kwargs: Any,
|
38
38
|
) -> None:
|
39
39
|
super().__init__()
|
@@ -42,9 +42,21 @@ class LLM(ABC, Generic[SettingsT, ConvertT]):
|
|
42
42
|
self._model_id = model_id or str(uuid4())[:8]
|
43
43
|
self._model_name = model_name
|
44
44
|
self._tools = {t.name: t for t in tools} if tools else None
|
45
|
-
self._response_format = response_format
|
46
45
|
self._llm_settings: SettingsT = llm_settings or cast("SettingsT", {})
|
47
46
|
|
47
|
+
self._response_format = response_format
|
48
|
+
self._response_format_pyd: (
|
49
|
+
TypeAdapter[Any] | Mapping[str, TypeAdapter[Any]] | None
|
50
|
+
)
|
51
|
+
if isinstance(response_format, type):
|
52
|
+
self._response_format_pyd = TypeAdapter(response_format)
|
53
|
+
elif isinstance(response_format, Mapping):
|
54
|
+
self._response_format_pyd = {
|
55
|
+
k: TypeAdapter(v) for k, v in response_format.items()
|
56
|
+
}
|
57
|
+
else:
|
58
|
+
self._response_format_pyd = None
|
59
|
+
|
48
60
|
@property
|
49
61
|
def model_id(self) -> str:
|
50
62
|
return self._model_id
|
@@ -62,7 +74,7 @@ class LLM(ABC, Generic[SettingsT, ConvertT]):
|
|
62
74
|
return self._tools
|
63
75
|
|
64
76
|
@property
|
65
|
-
def response_format(self) -> type | None:
|
77
|
+
def response_format(self) -> type | Mapping[str, type] | None:
|
66
78
|
return self._response_format
|
67
79
|
|
68
80
|
@tools.setter
|
@@ -114,7 +114,7 @@ def to_api_assistant_message(
|
|
114
114
|
tool_calls=api_tool_calls or [],
|
115
115
|
refusal=message.refusal,
|
116
116
|
)
|
117
|
-
if message.content is None
|
117
|
+
if message.content is None:
|
118
118
|
# Some API providers return None in the generated content without errors,
|
119
119
|
# even though None in the input content is not accepted.
|
120
120
|
api_message["content"] = "<empty>"
|
@@ -6,14 +6,9 @@ from collections.abc import Coroutine, Mapping
|
|
6
6
|
from datetime import datetime
|
7
7
|
from logging import getLogger
|
8
8
|
from pathlib import Path
|
9
|
-
from typing import Any, TypeVar
|
10
|
-
|
11
|
-
from pydantic import
|
12
|
-
GetCoreSchemaHandler,
|
13
|
-
TypeAdapter,
|
14
|
-
ValidationError,
|
15
|
-
)
|
16
|
-
from pydantic_core import core_schema
|
9
|
+
from typing import Any, TypeVar, overload
|
10
|
+
|
11
|
+
from pydantic import TypeAdapter, ValidationError
|
17
12
|
from tqdm.autonotebook import tqdm
|
18
13
|
|
19
14
|
logger = getLogger(__name__)
|
@@ -62,9 +57,37 @@ def parse_json_or_py_substring(
|
|
62
57
|
)
|
63
58
|
|
64
59
|
|
60
|
+
@overload
|
61
|
+
def validate_obj_from_json_or_py_string(
|
62
|
+
s: str,
|
63
|
+
adapter: TypeAdapter[T],
|
64
|
+
from_substring: bool = False,
|
65
|
+
) -> T: ...
|
66
|
+
|
67
|
+
|
68
|
+
@overload
|
69
|
+
def validate_obj_from_json_or_py_string(
|
70
|
+
s: str,
|
71
|
+
adapter: Mapping[str, TypeAdapter[T]],
|
72
|
+
from_substring: bool = False,
|
73
|
+
) -> T | str: ...
|
74
|
+
|
75
|
+
|
65
76
|
def validate_obj_from_json_or_py_string(
|
66
|
-
s: str,
|
67
|
-
|
77
|
+
s: str,
|
78
|
+
adapter: TypeAdapter[T] | Mapping[str, TypeAdapter[T]],
|
79
|
+
from_substring: bool = False,
|
80
|
+
) -> T | str:
|
81
|
+
_selected_adapter: TypeAdapter[T] | None = None
|
82
|
+
if isinstance(adapter, Mapping):
|
83
|
+
for _marker, _adapter in adapter.items():
|
84
|
+
if _marker in s:
|
85
|
+
_selected_adapter = _adapter
|
86
|
+
if _selected_adapter is None:
|
87
|
+
return s
|
88
|
+
else:
|
89
|
+
_selected_adapter = adapter
|
90
|
+
|
68
91
|
try:
|
69
92
|
if from_substring:
|
70
93
|
parsed = parse_json_or_py_substring(s, return_none_on_failure=True)
|
@@ -72,10 +95,11 @@ def validate_obj_from_json_or_py_string(
|
|
72
95
|
parsed = parse_json_or_py_string(s, return_none_on_failure=True)
|
73
96
|
if parsed is None:
|
74
97
|
parsed = s
|
75
|
-
return
|
98
|
+
return _selected_adapter.validate_python(parsed)
|
76
99
|
except (json.JSONDecodeError, ValidationError) as exc:
|
77
100
|
raise ValueError(
|
78
|
-
f"Invalid JSON or Python string:\n{s}\
|
101
|
+
f"Invalid JSON or Python string:\n{s}\n"
|
102
|
+
f"Expected type: {_selected_adapter._type}", # type: ignore[arg-type]
|
79
103
|
) from exc
|
80
104
|
|
81
105
|
|
@@ -89,64 +113,6 @@ def extract_xml_list(text: str) -> list[str]:
|
|
89
113
|
return chunks
|
90
114
|
|
91
115
|
|
92
|
-
def build_marker_json_parser_type(
|
93
|
-
marker_to_model: Mapping[str, type],
|
94
|
-
) -> type:
|
95
|
-
"""
|
96
|
-
Return a Pydantic-compatible *type* that, when given a **str**, searches for
|
97
|
-
the first marker substring and validates the JSON that follows with the
|
98
|
-
corresponding Pydantic model.
|
99
|
-
|
100
|
-
If no marker is found, the raw string is returned unchanged.
|
101
|
-
|
102
|
-
Example:
|
103
|
-
-------
|
104
|
-
>>> Todo = build_marker_json_parser_type({'```json': MyModel})
|
105
|
-
>>> Todo.validate('```json {"a": 1}')
|
106
|
-
MyModel(a=1)
|
107
|
-
|
108
|
-
"""
|
109
|
-
|
110
|
-
class MarkerParsedOutput:
|
111
|
-
"""String → (Model | str) parser generated by build_marker_json_parser_type."""
|
112
|
-
|
113
|
-
@classmethod
|
114
|
-
def __get_pydantic_core_schema__(
|
115
|
-
cls,
|
116
|
-
_source_type: Any,
|
117
|
-
_handler: GetCoreSchemaHandler,
|
118
|
-
) -> core_schema.CoreSchema:
|
119
|
-
def _validate(value: Any) -> Any:
|
120
|
-
if not isinstance(value, str):
|
121
|
-
raise TypeError("MarkerParsedOutput expects a string")
|
122
|
-
|
123
|
-
for marker, model in marker_to_model.items():
|
124
|
-
if marker in value:
|
125
|
-
adapter = TypeAdapter[Any](model)
|
126
|
-
return validate_obj_from_json_or_py_string(
|
127
|
-
value, adapter=adapter, from_substring=True
|
128
|
-
)
|
129
|
-
|
130
|
-
return value
|
131
|
-
|
132
|
-
return core_schema.no_info_after_validator_function(
|
133
|
-
_validate, core_schema.any_schema()
|
134
|
-
)
|
135
|
-
|
136
|
-
@classmethod
|
137
|
-
def __get_pydantic_json_schema__(
|
138
|
-
cls,
|
139
|
-
schema: core_schema.CoreSchema,
|
140
|
-
handler: GetCoreSchemaHandler,
|
141
|
-
):
|
142
|
-
return handler(schema)
|
143
|
-
|
144
|
-
unique_suffix = "_".join(sorted(marker_to_model))[:40]
|
145
|
-
MarkerParsedOutput.__name__ = f"MarkerParsedOutput_{unique_suffix}"
|
146
|
-
|
147
|
-
return MarkerParsedOutput
|
148
|
-
|
149
|
-
|
150
116
|
def read_txt(file_path: str | Path, encoding: str = "utf-8") -> str:
|
151
117
|
return Path(file_path).read_text(encoding=encoding)
|
152
118
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{grasp_agents-0.2.6 → grasp_agents-0.2.8}/src/grasp_agents/rate_limiting/rate_limiter_chunked.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|