llm-codegen-research 2.13__tar.gz → 2.14__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.
Files changed (46) hide show
  1. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/PKG-INFO +1 -1
  2. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/__init__.py +10 -1
  3. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/openai_tool.py +77 -7
  4. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_codegen_research.egg-info/PKG-INFO +1 -1
  5. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/LICENSE +0 -0
  6. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/README.md +0 -0
  7. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/pyproject.toml +0 -0
  8. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/setup.cfg +0 -0
  9. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/__init__.py +0 -0
  10. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/__init__.py +0 -0
  11. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/classes.py +0 -0
  12. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/languages/__init__.py +0 -0
  13. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/languages/code_data.py +0 -0
  14. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/languages/javascript.py +0 -0
  15. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/languages/python.py +0 -0
  16. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/languages/rust.py +0 -0
  17. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/analyse/regexes.py +0 -0
  18. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/decorators.py +0 -0
  19. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/defaults.py +0 -0
  20. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/enums.py +0 -0
  21. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/json_utils.py +0 -0
  22. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/__init__.py +0 -0
  23. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/anthropic.py +0 -0
  24. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/base.py +0 -0
  25. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/deepseek.py +0 -0
  26. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/mistral.py +0 -0
  27. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/nscale.py +0 -0
  28. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/openai.py +0 -0
  29. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/protocol.py +0 -0
  30. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/clients/together.py +0 -0
  31. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/generate.py +0 -0
  32. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/llm/prompts.py +0 -0
  33. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/py.typed +0 -0
  34. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/scripts/test_cuda.py +0 -0
  35. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_cgr/timeout.py +0 -0
  36. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_codegen_research.egg-info/SOURCES.txt +0 -0
  37. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_codegen_research.egg-info/dependency_links.txt +0 -0
  38. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_codegen_research.egg-info/entry_points.txt +0 -0
  39. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_codegen_research.egg-info/requires.txt +0 -0
  40. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/src/llm_codegen_research.egg-info/top_level.txt +0 -0
  41. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/tests/test_enums.py +0 -0
  42. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/tests/test_json_utils.py +0 -0
  43. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/tests/test_llm_api.py +0 -0
  44. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/tests/test_llm_local.py +0 -0
  45. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/tests/test_llm_tool.py +0 -0
  46. {llm_codegen_research-2.13 → llm_codegen_research-2.14}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llm-codegen-research
3
- Version: 2.13
3
+ Version: 2.14
4
4
  Summary: Useful classes and methods for researching code-generation by LLMs.
5
5
  Author-email: Lukas Twist <itsluketwist@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/itsluketwist/llm-codegen-research
@@ -6,7 +6,12 @@ from llm_cgr.llm.clients.deepseek import DeepSeek_LLM
6
6
  from llm_cgr.llm.clients.mistral import Mistral_LLM
7
7
  from llm_cgr.llm.clients.nscale import Nscale_LLM
8
8
  from llm_cgr.llm.clients.openai import OpenAI_LLM
9
- from llm_cgr.llm.clients.openai_tool import OpenAI_Tool_LLM, Tool
9
+ from llm_cgr.llm.clients.openai_tool import (
10
+ MAX_TOOL_CALLS,
11
+ MAX_TOOL_ITERATIONS,
12
+ OpenAI_Tool_LLM,
13
+ Tool,
14
+ )
10
15
  from llm_cgr.llm.clients.protocol import GenerationProtocol
11
16
  from llm_cgr.llm.clients.together import TogetherAI_LLM
12
17
 
@@ -29,6 +34,8 @@ def get_llm(
29
34
  max_tokens: int | None = None,
30
35
  provider: str | None = None,
31
36
  tools: list[Tool] | None = None,
37
+ max_tool_iterations: int = MAX_TOOL_ITERATIONS,
38
+ max_tool_calls: int = MAX_TOOL_CALLS,
32
39
  ) -> GenerationProtocol:
33
40
  """
34
41
  Initialise the correct LLM client for the given model.
@@ -63,6 +70,8 @@ def get_llm(
63
70
  temperature=temperature,
64
71
  top_p=top_p,
65
72
  max_tokens=max_tokens,
73
+ max_tool_iterations=max_tool_iterations,
74
+ max_tool_calls=max_tool_calls,
66
75
  )
67
76
 
68
77
  return llm_class(
@@ -10,8 +10,11 @@ from openai.types.responses import ResponseFunctionToolCall, ResponseInputItemPa
10
10
  from llm_cgr.llm.clients.openai import OpenAI_LLM
11
11
 
12
12
 
13
- # maximum number of tool-call iterations per request, to prevent runaway loops
14
- MAX_TOOL_ITERATIONS: int = 10
13
+ # maximum tool-call rounds allowed within a single generate() or chat() call
14
+ MAX_TOOL_ITERATIONS: int = 5
15
+
16
+ # maximum total tool calls allowed across the lifetime of a client instance
17
+ MAX_TOOL_CALLS: int = 10
15
18
 
16
19
 
17
20
  @dataclass
@@ -51,11 +54,17 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
51
54
  temperature: float | None = None,
52
55
  top_p: float | None = None,
53
56
  max_tokens: int | None = None,
57
+ max_tool_iterations: int = MAX_TOOL_ITERATIONS,
58
+ max_tool_calls: int = MAX_TOOL_CALLS,
54
59
  ) -> None:
55
60
  """
56
61
  Initialise the OpenAI tool client.
57
62
 
58
63
  Requires the OPENAI_API_KEY environment variable to be set.
64
+ max_tool_iterations caps tool-call rounds within a single request.
65
+ max_tool_calls caps the cumulative total across all requests on this
66
+ instance. When either limit is reached, the model is sent a message
67
+ asking it to answer immediately without any further tool calls.
59
68
  """
60
69
  super().__init__(
61
70
  model=model,
@@ -65,6 +74,8 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
65
74
  max_tokens=max_tokens,
66
75
  )
67
76
  self._tools = tools
77
+ self._max_tool_iterations = max_tool_iterations
78
+ self._max_tool_calls = max_tool_calls
68
79
  # cumulative count of individual tool calls made by this instance
69
80
  self._tool_calls: int = 0
70
81
 
@@ -90,6 +101,43 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
90
101
  "parameters": tool.parameters,
91
102
  }
92
103
 
104
+ def _force_final_answer(
105
+ self,
106
+ current_input: list[Any],
107
+ model: str,
108
+ temperature: float | None,
109
+ top_p: float | None,
110
+ max_tokens: int | None,
111
+ ) -> str:
112
+ """Force the model to produce a text answer after a limit is reached.
113
+
114
+ Appends a user message telling the model it has used all its allowed
115
+ tool calls, then calls the API one final time without any tools so the
116
+ model cannot make further calls.
117
+
118
+ Returns the model's final text response.
119
+ """
120
+ # tell the model it must answer now — no more tool calls are allowed
121
+ current_input.append(
122
+ self._build_message(
123
+ role="user",
124
+ content=(
125
+ "You have reached the maximum number of tool calls allowed. "
126
+ "Please provide your final answer now based on the information "
127
+ "you have gathered, without calling any more tools."
128
+ ),
129
+ )
130
+ )
131
+ response = self._client.responses.create(
132
+ input=cast(list[ResponseInputItemParam], current_input),
133
+ model=model,
134
+ temperature=temperature if temperature is not None else openai.omit,
135
+ top_p=top_p if top_p is not None else openai.omit,
136
+ max_output_tokens=max_tokens if max_tokens is not None else openai.omit,
137
+ # no tools provided: the model cannot make further tool calls
138
+ )
139
+ return response.output_text
140
+
93
141
  def _run_tool_loop(
94
142
  self,
95
143
  messages: list[dict[str, Any]],
@@ -101,8 +149,12 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
101
149
  """Run the agentic tool-call loop for a single turn.
102
150
 
103
151
  Calls the OpenAI API in a loop, executing any tool calls the model
104
- requests, until the model produces a final text response or the
105
- MAX_TOOL_ITERATIONS safety limit is reached.
152
+ requests, until the model produces a final text response or a limit is
153
+ reached. Two limits apply:
154
+ - max_tool_iterations: rounds allowed within this single call.
155
+ - max_tool_calls: cumulative total across all calls on this instance.
156
+ When either limit is hit, _force_final_answer() is called, which tells
157
+ the model to answer immediately without making any further tool calls.
106
158
 
107
159
  Returns the final text response.
108
160
  """
@@ -118,7 +170,7 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
118
170
  # and the richer tool-call dicts without fighting the type checker.
119
171
  current_input: list[Any] = list(messages)
120
172
 
121
- for _ in range(MAX_TOOL_ITERATIONS):
173
+ for _ in range(self._max_tool_iterations):
122
174
  response = self._client.responses.create(
123
175
  input=cast(list[ResponseInputItemParam], current_input),
124
176
  model=model,
@@ -137,6 +189,18 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
137
189
  if not function_calls:
138
190
  return response.output_text
139
191
 
192
+ # check the overall cumulative limit before processing these calls.
193
+ # if adding them would exceed the limit, force a final answer now
194
+ # without executing any of the pending tool calls.
195
+ if self._tool_calls + len(function_calls) > self._max_tool_calls:
196
+ return self._force_final_answer(
197
+ current_input=current_input,
198
+ model=model,
199
+ temperature=temperature,
200
+ top_p=top_p,
201
+ max_tokens=max_tokens,
202
+ )
203
+
140
204
  # increment the cumulative counter; parallel calls count individually
141
205
  self._tool_calls += len(function_calls)
142
206
 
@@ -172,8 +236,14 @@ class OpenAI_Tool_LLM(OpenAI_LLM):
172
236
 
173
237
  # loop continues: enriched input is sent back to the model
174
238
 
175
- # safety fallback: return whatever text the model produced on the last turn
176
- return response.output_text
239
+ # max_tool_iterations exhausted force the model to answer now
240
+ return self._force_final_answer(
241
+ current_input=current_input,
242
+ model=model,
243
+ temperature=temperature,
244
+ top_p=top_p,
245
+ max_tokens=max_tokens,
246
+ )
177
247
 
178
248
  def generate(
179
249
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llm-codegen-research
3
- Version: 2.13
3
+ Version: 2.14
4
4
  Summary: Useful classes and methods for researching code-generation by LLMs.
5
5
  Author-email: Lukas Twist <itsluketwist@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/itsluketwist/llm-codegen-research