Skip to content

Commit dfc3386

Browse files
authored
Update AgenticLLM to allow model difference between assistant and summariser (#1383)
* update agentics model usage; add location to google api * bump version
1 parent 767c1cd commit dfc3386

File tree

6 files changed

+72
-41
lines changed

6 files changed

+72
-41
lines changed

patchwork/common/client/llm/aio.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,21 @@ def chat_completion(
203203
def create_aio_client(inputs) -> "AioLlmClient" | None:
204204
clients = []
205205

206+
client_args = {key[len("client_") :]: value for key, value in inputs.items() if key.startswith("client_")}
207+
206208
patched_key = inputs.get("patched_api_key")
207209
if patched_key is not None:
208210
client = OpenAiLlmClient(patched_key, DEFAULT_PATCH_URL)
209211
clients.append(client)
210212

211213
openai_key = inputs.get("openai_api_key") or os.environ.get("OPENAI_API_KEY")
212214
if openai_key is not None:
213-
client_args = {key[len("client_") :]: value for key, value in inputs.items() if key.startswith("client_")}
214215
client = OpenAiLlmClient(openai_key, **client_args)
215216
clients.append(client)
216217

217218
google_key = inputs.get("google_api_key")
218219
if google_key is not None:
219-
client = GoogleLlmClient(google_key)
220+
client = GoogleLlmClient(google_key, **client_args)
220221
clients.append(client)
221222

222223
anthropic_key = inputs.get("anthropic_api_key")

patchwork/common/client/llm/google.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ class GoogleLlmClient(LlmClient):
5454
]
5555
__MODEL_PREFIX = "models/"
5656

57-
def __init__(self, api_key: str):
57+
def __init__(self, api_key: str, location: Optional[str] = None):
5858
self.__api_key = api_key
59-
self.client = genai.Client(api_key=api_key)
59+
self.__location = location
60+
self.client = genai.Client(api_key=api_key, location=location)
6061

6162
@lru_cache(maxsize=1)
6263
def __get_models_info(self) -> list[Model]:
@@ -69,7 +70,11 @@ def __get_pydantic_model(self, model_settings: ModelSettings | None) -> Model:
6970
if model_name is None:
7071
raise ValueError("Model must be set cannot be None")
7172

72-
return GeminiModel(model_name, api_key=self.__api_key)
73+
if self.__location is None:
74+
return GeminiModel(model_name, api_key=self.__api_key)
75+
76+
url_template = f"https://{self.__location}-generativelanguage.googleapis.com/v1beta/models/{{model}}:"
77+
return GeminiModel(model_name, api_key=self.__api_key, url_template=url_template)
7378

7479
async def request(
7580
self,

patchwork/common/multiturn_strategy/agentic_strategy_v2.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Config:
2525
arbitrary_types_allowed = True
2626

2727
name: str
28+
model: str
2829
tool_set: Dict[str, Tool]
2930
system_prompt: str = ""
3031
example_json: Union[str, Dict[str, Any]] = DEFAULT_AGENT_EXAMPLE_JSON
@@ -41,15 +42,15 @@ def model_post_init(self, __context: Any) -> None:
4142

4243
class AgenticStrategyV2:
4344
def __init__(
44-
self,
45-
model: str,
46-
llm_client: LlmClient,
47-
template_data: dict[str, str],
48-
system_prompt_template: str,
49-
user_prompt_template: str,
50-
agent_configs: list[AgentConfig],
51-
example_json: Union[str, dict[str, Any]] = '{"output":"output text"}',
52-
limit: Optional[int] = None,
45+
self,
46+
model: str,
47+
llm_client: LlmClient,
48+
template_data: dict[str, str],
49+
system_prompt_template: str,
50+
user_prompt_template: str,
51+
agent_configs: list[AgentConfig],
52+
example_json: Union[str, dict[str, Any]] = '{"output":"output text"}',
53+
limit: Optional[int] = None,
5354
):
5455
self.__limit = limit
5556
self.__template_data = template_data
@@ -76,7 +77,7 @@ def __init__(
7677
result_type=example_json_to_base_model(agent_config.example_json),
7778
model_settings=dict(
7879
parallel_tool_calls=False,
79-
model=model,
80+
model=agent_config.model,
8081
),
8182
)
8283

@@ -151,7 +152,7 @@ def execute(self, limit: Optional[int] = None) -> dict:
151152
self.__summariser.run(
152153
"Please give me the result from the following summary of what the assistants have done."
153154
+ agent_summary_list,
154-
)
155+
)
155156
)
156157
self.__request_tokens += final_result.usage().request_tokens or 0
157158
self.__response_tokens += final_result.usage().response_tokens or 0

patchwork/common/tools/grep_tool.py

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def json_schema(self) -> dict:
114114
return {
115115
"name": "find_text",
116116
"description": f"""\
117-
Tool to find text in a file using a pattern based on the Unix shell style.
117+
Tool to find text in a file or files in a directory using a pattern based on the Unix shell style.
118118
The current working directory is always {self.__working_dir}.
119119
The path provided should either be absolute or relative to the current working directory.
120120
@@ -124,10 +124,6 @@ def json_schema(self) -> dict:
124124
"input_schema": {
125125
"type": "object",
126126
"properties": {
127-
"path": {
128-
"description": "The path to the file to find text in.",
129-
"type": "string",
130-
},
131127
"pattern": {
132128
"description": """\
133129
The Unix shell style pattern to match files using.
@@ -141,6 +137,14 @@ def json_schema(self) -> dict:
141137
Example:
142138
* '*macs' will match the file '.emacs'
143139
* '*.py' will match all files with the '.py' extension
140+
""",
141+
"type": "string",
142+
},
143+
"path": {
144+
"description": """\
145+
The path to the file to find text in.
146+
If not given, will search all file content in the current working directory.
147+
If the path is a directory, will search all file content in the directory.
144148
""",
145149
"type": "string",
146150
},
@@ -149,22 +153,22 @@ def json_schema(self) -> dict:
149153
"type": "boolean",
150154
},
151155
},
152-
"required": ["path", "pattern"],
156+
"required": ["pattern"],
153157
},
154158
}
155159

156160
def execute(
157-
self,
158-
path: Optional[Path] = None,
159-
pattern: Optional[str] = None,
160-
is_case_sensitive: bool = False,
161+
self,
162+
pattern: Optional[str] = None,
163+
path: Optional[Path] = None,
164+
is_case_sensitive: bool = False,
161165
) -> str:
162-
if path is None:
163-
raise ValueError("Path argument is required!")
164-
165166
if pattern is None:
166167
raise ValueError("pattern argument is required!")
167168

169+
if path is None:
170+
path = Path(self.__working_dir)
171+
168172
matcher = fnmatch.fnmatch
169173
if is_case_sensitive:
170174
matcher = fnmatch.fnmatchcase
@@ -173,15 +177,34 @@ def execute(
173177
if not path.is_relative_to(self.__working_dir):
174178
raise ValueError("Path must be relative to working dir")
175179

176-
matches = []
177-
with path.open("r") as f:
178-
for i, line in enumerate(f.readlines()):
179-
if not matcher(line, pattern):
180-
continue
180+
if path.is_file():
181+
paths = [path]
182+
else:
183+
paths = [p for p in path.iterdir() if p.is_file()]
184+
185+
from collections import defaultdict
186+
file_matches = defaultdict(list)
187+
for path in paths:
188+
with path.open("r") as f:
189+
for i, line in enumerate(f.readlines()):
190+
if not matcher(line, pattern):
191+
continue
192+
193+
content = f"Line {i + 1}: {line}"
194+
if len(line) > self.__CHAR_LIMIT:
195+
content = f"Line {i + 1}: {self.__CHAR_LIMIT_TEXT}"
196+
197+
file_matches[str(path)].append(content)
198+
199+
total_file_matches = ""
200+
for path_str, matches in file_matches.items():
201+
total_file_matches += f"\nPattern matches found in '{path}':\n" + "\n".join(matches)
202+
203+
if len(total_file_matches) <= 5000:
204+
return total_file_matches
181205

182-
content = f"Line {i + 1}: {line}"
183-
if len(line) > self.__CHAR_LIMIT:
184-
content = f"Line {i + 1}: {self.__CHAR_LIMIT_TEXT}"
206+
total_file_matches = ""
207+
for path_str, matches in file_matches.items():
208+
total_file_matches += f"\n {len(matches)} Pattern matches found in '{path}': <TRUNCATED>\n"
209+
return total_file_matches
185210

186-
matches.append(content)
187-
return f"Pattern matches found in '{path}':\n" + "\n".join(matches)

patchwork/steps/AgenticLLMV2/AgenticLLMV2.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@ def __init__(self, inputs):
1818
base_path = str(Path.cwd())
1919
self.conversation_limit = int(inputs.get("max_agent_calls", 1))
2020
self.agentic_strategy = AgenticStrategyV2(
21-
model="claude-3-7-sonnet-latest",
21+
model="claude-3-5-sonnet-latest",
2222
llm_client=AioLlmClient.create_aio_client(inputs),
2323
template_data=inputs.get("prompt_value", {}),
2424
system_prompt_template=inputs.get("system_prompt", "Summarise from our previous conversation"),
2525
user_prompt_template=inputs.get("user_prompt"),
2626
agent_configs=[
2727
AgentConfig(
2828
name="Assistant",
29+
model="claude-3-7-sonnet-latest",
2930
tool_set=Tool.get_tools(path=base_path),
3031
system_prompt=inputs.get("agent_system_prompt"),
3132
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "patchwork-cli"
3-
version = "0.0.105"
3+
version = "0.0.106"
44
description = ""
55
authors = ["patched.codes"]
66
license = "AGPL"

0 commit comments

Comments
 (0)