versionhq 1.1.6.4__py3-none-any.whl → 1.1.7.1__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.
versionhq/__init__.py CHANGED
@@ -17,7 +17,7 @@ from versionhq.team.model import Team, TeamOutput
17
17
  from versionhq.tool.model import Tool
18
18
 
19
19
 
20
- __version__ = "1.1.6.4"
20
+ __version__ = "1.1.7.1"
21
21
  __all__ = [
22
22
  "Agent",
23
23
  "Customer",
@@ -7,18 +7,10 @@ class UsageMetrics(BaseModel):
7
7
  """
8
8
 
9
9
  total_tokens: int = Field(default=0, description="Total number of tokens used")
10
- prompt_tokens: int = Field(
11
- default=0, description="Number of tokens used in prompts"
12
- )
13
- cached_prompt_tokens: int = Field(
14
- default=0, description="Number of cached prompt tokens used"
15
- )
16
- completion_tokens: int = Field(
17
- default=0, description="Number of tokens used in completions"
18
- )
19
- successful_requests: int = Field(
20
- default=0, description="Number of successful requests made"
21
- )
10
+ prompt_tokens: int = Field(default=0, description="Number of tokens used in prompts")
11
+ cached_prompt_tokens: int = Field(default=0, description="Number of cached prompt tokens used")
12
+ completion_tokens: int = Field(default=0, description="Number of tokens used in completions")
13
+ successful_requests: int = Field(default=0, description="Number of successful requests made")
22
14
 
23
15
  def add_usage_metrics(self, usage_metrics: "UsageMetrics"):
24
16
  """
@@ -0,0 +1,3 @@
1
+ BACKSTORY="""You are a {role} with deep understanding of {knowledge} and highly skilled in {skillsets}.
2
+ You have access to call the RAG tools that can {rag_tool_overview}. By leveraging these tools, your knowledge, and skillsets, you can identify competitive strategies that have been proven effective to achieve the goal: {goal}. Take these into consideration, create innovative solutions.
3
+ """
File without changes
versionhq/agent/model.py CHANGED
@@ -3,7 +3,8 @@ import uuid
3
3
  from abc import ABC
4
4
  from typing import Any, Dict, List, Optional, TypeVar, Union
5
5
  from dotenv import load_dotenv
6
- from pydantic import UUID4, BaseModel, Field, InstanceOf, PrivateAttr, model_validator
6
+ from pydantic import UUID4, BaseModel, Field, InstanceOf, PrivateAttr, model_validator, field_validator
7
+ from pydantic_core import PydanticCustomError
7
8
 
8
9
  from versionhq._utils.cache_handler import CacheHandler
9
10
  from versionhq._utils.logger import Logger
@@ -77,8 +78,9 @@ class TokenProcess:
77
78
  # @track_agent()
78
79
  class Agent(ABC, BaseModel):
79
80
  """
80
- Base class for third-party agents that LLM runs.
81
- The agent can execute tasks alone or team. When the agent belongs to team, it needs to prioritize the team.
81
+ Agent class that run on LLM.
82
+ Agents execute tasks alone or in the team, using RAG tools and knowledge base if any.
83
+ Agents will prioritize team tasks when they belong to the team.
82
84
  * (Temp) Comment out all the optional fields except for Team and LLM settings for convenience.
83
85
  """
84
86
 
@@ -90,96 +92,68 @@ class Agent(ABC, BaseModel):
90
92
  _times_executed: int = PrivateAttr(default=0)
91
93
 
92
94
  id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
93
- agent_ops_agent_name: str = None
94
- agent_ops_agent_id: str = None
95
95
  role: str = Field(description="role of the agent - used in summary and logs")
96
- goal: str = Field(
97
- description="concise goal of the agent (details are set in the Task instance)"
98
- )
99
- backstory: str = Field(description="context passed to the LLM")
96
+ goal: str = Field(description="concise goal of the agent (details are set in the Task instance)")
97
+ backstory: Optional[str] = Field(default=None, description="system context passed to the LLM")
98
+ knowledge: Optional[str] = Field(default=None)
99
+ skillsets: Optional[List[str]] = Field(default_factory=list)
100
100
 
101
101
  # tools
102
102
  tools: Optional[List[Any]] = Field(default_factory=list)
103
- tool_handler: InstanceOf[ToolHandler] = Field(
104
- default=None, description="handle tool cache and last used tool"
105
- )
106
-
107
- # team, rules of task executions
108
- team: Optional[List[Any]] = Field(
109
- default=None, description="Team to which the agent belongs"
110
- )
111
- allow_delegation: bool = Field(
112
- default=False,
113
- description="Enable agent to delegate and ask questions among each other",
114
- )
115
- allow_code_execution: Optional[bool] = Field(
116
- default=False, description="Enable code execution for the agent."
117
- )
118
- max_retry_limit: int = Field(
119
- default=2,
120
- description="max. number of retries for the task execution when an error occurs. cascaed to the `invoke` function",
121
- )
122
- max_iter: Optional[int] = Field(
123
- default=25,
124
- description="max. number of iterations for an agent to execute a task",
125
- )
126
- step_callback: Optional[Any] = Field(
127
- default=None,
128
- description="Callback to be executed after each step of the agent execution",
129
- )
103
+ tool_handler: InstanceOf[ToolHandler] = Field(default=None, description="handle tool cache and last used tool")
104
+
105
+ # team, task execution rules
106
+ team: Optional[List[Any]] = Field(default=None, description="Team to which the agent belongs")
107
+ allow_delegation: bool = Field(default=False,description="Enable agent to delegate and ask questions among each other")
108
+ allow_code_execution: Optional[bool] = Field(default=False, description="Enable code execution for the agent.")
109
+ max_retry_limit: int = Field(default=2,description="max. number of retries for the task execution when an error occurs. cascaed to the `invoke` function")
110
+ max_iter: Optional[int] = Field(default=25,description="max. number of iterations for an agent to execute a task")
111
+ step_callback: Optional[Any] = Field(default=None,description="Callback to be executed after each step of the agent execution")
130
112
 
131
113
  # llm settings cascaded to the LLM model
132
114
  llm: Union[str, InstanceOf[LLM], Any] = Field(default=None)
133
115
  function_calling_llm: Union[str, InstanceOf[LLM], Any] = Field(default=None)
134
- respect_context_window: bool = Field(
135
- default=True,
136
- description="Keep messages under the context window size by summarizing content",
137
- )
138
- max_tokens: Optional[int] = Field(
139
- default=None, description="max. number of tokens for the agent's execution"
140
- )
141
- max_execution_time: Optional[int] = Field(
142
- default=None, description="max. execution time for an agent to execute a task"
143
- )
144
- max_rpm: Optional[int] = Field(
145
- default=None,
146
- description="max. number of requests per minute for the agent execution",
147
- )
116
+ respect_context_window: bool = Field(default=True,description="Keep messages under the context window size by summarizing content")
117
+ max_tokens: Optional[int] = Field(default=None, description="max. number of tokens for the agent's execution")
118
+ max_execution_time: Optional[int] = Field(default=None, description="max. execution time for an agent to execute a task")
119
+ max_rpm: Optional[int] = Field(default=None, description="max. number of requests per minute for the agent execution")
148
120
 
149
121
  # prompt rules
150
- use_system_prompt: Optional[bool] = Field(
151
- default=True, description="Use system prompt for the agent"
152
- )
153
- system_template: Optional[str] = Field(
154
- default=None, description="System format for the agent."
155
- )
156
- prompt_template: Optional[str] = Field(
157
- default=None, description="Prompt format for the agent."
158
- )
159
- response_template: Optional[str] = Field(
160
- default=None, description="Response format for the agent."
161
- )
122
+ use_system_prompt: Optional[bool] = Field(default=True, description="Use system prompt for the agent")
123
+ system_template: Optional[str] = Field(default=None, description="System format for the agent.")
124
+ prompt_template: Optional[str] = Field(default=None, description="Prompt format for the agent.")
125
+ response_template: Optional[str] = Field(default=None, description="Response format for the agent.")
162
126
 
163
127
  # config, cache, error handling
164
- config: Optional[Dict[str, Any]] = Field(
165
- default=None, exclude=True, description="Configuration for the agent"
166
- )
167
- cache: bool = Field(
168
- default=True, description="Whether the agent should use a cache for tool usage."
169
- )
170
- cache_handler: InstanceOf[CacheHandler] = Field(
171
- default=None, description="An instance of the CacheHandler class."
172
- )
173
- formatting_errors: int = Field(
174
- default=0, description="Number of formatting errors."
175
- )
176
- verbose: bool = Field(
177
- default=True, description="Verbose mode for the Agent Execution"
178
- )
128
+ config: Optional[Dict[str, Any]] = Field(default=None, exclude=True, description="Configuration for the agent")
129
+ cache: bool = Field(default=True, description="Whether the agent should use a cache for tool usage.")
130
+ cache_handler: InstanceOf[CacheHandler] = Field(default=None, description="An instance of the CacheHandler class.")
131
+ formatting_errors: int = Field(default=0, description="Number of formatting errors.")
132
+ verbose: bool = Field(default=True, description="Verbose mode for the Agent Execution")
133
+ agent_ops_agent_name: str = None
134
+ agent_ops_agent_id: str = None
135
+
179
136
 
180
137
  def __repr__(self):
181
138
  return f"Agent(role={self.role}, goal={self.goal}, backstory={self.backstory})"
182
139
 
140
+
141
+ @field_validator("id", mode="before")
142
+ @classmethod
143
+ def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
144
+ if v:
145
+ raise PydanticCustomError("may_not_set_field", "This field is not to be set by the user.", {})
146
+
147
+
148
+ @model_validator(mode="after")
149
+ def validate_required_fields(self):
150
+ required_fields = ["role", "goal"]
151
+ for field in required_fields:
152
+ if getattr(self, field) is None:
153
+ raise ValueError( f"{field} must be provided either directly or through config")
154
+ return self
155
+
156
+
183
157
  @model_validator(mode="after")
184
158
  def set_up_llm(self):
185
159
  """
@@ -189,61 +163,27 @@ class Agent(ABC, BaseModel):
189
163
  """
190
164
 
191
165
  self.agent_ops_agent_name = self.role
192
- unaccepted_attributes = [
193
- "AWS_ACCESS_KEY_ID",
194
- "AWS_SECRET_ACCESS_KEY",
195
- "AWS_REGION_NAME",
196
- ]
197
- callbacks = (
198
- [
199
- self.step_callback,
200
- ]
201
- if self.step_callback is not None
202
- else []
203
- )
166
+ unaccepted_attributes = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_REGION_NAME"]
167
+ callbacks = ([self.step_callback,]if self.step_callback is not None else [])
204
168
 
205
169
  if isinstance(self.llm, LLM):
206
170
  self.llm.timeout = self.max_execution_time
207
171
  self.llm.max_tokens = self.max_tokens
208
- self.llm.context_window_size = (
209
- self.llm.get_context_window_size()
210
- if self.respect_context_window == True
211
- else DEFAULT_CONTEXT_WINDOW
212
- )
172
+ self.llm.context_window_size = (self.llm.get_context_window_size() if self.respect_context_window == True else DEFAULT_CONTEXT_WINDOW)
213
173
  self.llm.callbacks = callbacks
214
174
 
215
- elif isinstance(self.llm, str):
216
- self.llm = LLM(
217
- model=self.llm,
218
- timeout=self.max_execution_time,
219
- max_tokens=self.max_tokens,
220
- callbacks=callbacks,
221
- )
222
-
223
- context_window_size = (
224
- self.llm.get_context_window_size()
225
- if self.respect_context_window == True
226
- else DEFAULT_CONTEXT_WINDOW
227
- )
228
- self.llm.context_window_size = context_window_size
229
-
230
- elif self.llm is None:
231
- model_name = os.environ.get(
232
- "LITELLM_MODEL_NAME", os.environ.get("MODEL", "gpt-4o-mini")
233
- )
175
+ elif isinstance(self.llm, str) or self.llm is None:
176
+ model_name = os.environ.get("LITELLM_MODEL_NAME", os.environ.get("MODEL", "gpt-3.5-turbo"))
234
177
  llm_params = {
235
- "model": model_name,
178
+ "model": model_name if self.llm is None else self.llm,
236
179
  "timeout": self.max_execution_time,
237
180
  "max_tokens": self.max_tokens,
238
181
  "callbacks": callbacks,
182
+ "api_key": os.environ.get("LITELLM_API_KEY", None),
183
+ "base_url": os.environ.get("OPENAI_API_BASE", os.environ.get("OPENAI_BASE_URL", None))
239
184
  }
240
- api_base = os.environ.get(
241
- "OPENAI_API_BASE", os.environ.get("OPENAI_BASE_URL", None)
242
- )
243
- if api_base:
244
- llm_params["base_url"] = api_base
245
185
 
246
- set_provider = model_name.split("/")[0] if "/" in model_name else "openai"
186
+ set_provider = model_name.split("/")[0] if "/" in model_name else "openai" #! REFINEME
247
187
  for provider, env_vars in LLM_VARS.items():
248
188
  if provider == set_provider:
249
189
  for env_var in env_vars:
@@ -252,17 +192,9 @@ class Agent(ABC, BaseModel):
252
192
  if key_name and key_name not in unaccepted_attributes:
253
193
  env_value = os.environ.get(key_name)
254
194
  if env_value:
255
- key_name = (
256
- "api_key" if "API_KEY" in key_name else key_name
257
- )
258
- key_name = (
259
- "api_base" if "API_BASE" in key_name else key_name
260
- )
261
- key_name = (
262
- "api_version"
263
- if "API_VERSION" in key_name
264
- else key_name
265
- )
195
+ key_name = ("api_key" if "API_KEY" in key_name else key_name)
196
+ key_name = ("api_base" if "API_BASE" in key_name else key_name)
197
+ key_name = ("api_version" if "API_VERSION" in key_name else key_name)
266
198
  llm_params[key_name] = env_value
267
199
  elif env_var.get("default", False):
268
200
  for key, value in env_var.items():
@@ -270,34 +202,22 @@ class Agent(ABC, BaseModel):
270
202
  if key in os.environ:
271
203
  llm_params[key] = value
272
204
  self.llm = LLM(**llm_params)
273
- context_window_size = (
274
- self.llm.get_context_window_size()
275
- if self.respect_context_window == True
276
- else DEFAULT_CONTEXT_WINDOW
277
- )
205
+ context_window_size = (self.llm.get_context_window_size() if self.respect_context_window == True else DEFAULT_CONTEXT_WINDOW)
278
206
  self.llm.context_window_size = context_window_size
279
207
 
280
208
  else:
281
209
  llm_params = {
282
- "model": (
283
- getattr(self.llm, "model_name")
284
- or getattr(self.llm, "deployment_name")
285
- or str(self.llm)
286
- ),
287
- "max_tokens": (
288
- getattr(self.llm, "max_tokens") or self.max_tokens or 3000
289
- ),
210
+ "model": (getattr(self.llm, "model_name") or getattr(self.llm, "deployment_name") or str(self.llm)),
211
+ "max_tokens": (getattr(self.llm, "max_tokens") or self.max_tokens or 3000),
290
212
  "timeout": getattr(self.llm, "timeout", self.max_execution_time),
291
213
  "callbacks": getattr(self.llm, "callbacks") or callbacks,
292
214
  "temperature": getattr(self.llm, "temperature", None),
293
215
  "logprobs": getattr(self.llm, "logprobs", None),
294
- "api_key": getattr(self.llm, "api_key", None),
216
+ "api_key": getattr(self.llm, "api_key", os.environ.get("LITELLM_API_KEY", None)),
295
217
  "base_url": getattr(self.llm, "base_url", None),
296
218
  "organization": getattr(self.llm, "organization", None),
297
219
  }
298
- llm_params = {
299
- k: v for k, v in llm_params.items() if v is not None
300
- } # factor out None values
220
+ llm_params = { k: v for k, v in llm_params.items() if v is not None }
301
221
  self.llm = LLM(**llm_params)
302
222
 
303
223
  """
@@ -348,6 +268,7 @@ class Agent(ABC, BaseModel):
348
268
  )
349
269
  return self
350
270
 
271
+
351
272
  @model_validator(mode="after")
352
273
  def set_up_tools(self):
353
274
  """
@@ -371,13 +292,30 @@ class Agent(ABC, BaseModel):
371
292
 
372
293
  return self
373
294
 
295
+
296
+ @model_validator(mode="after")
297
+ def set_up_backstory(self):
298
+ """
299
+ Set up the backstory using a templated BACKSTORY when the backstory is None
300
+ """
301
+
302
+ if self.backstory is None:
303
+ from versionhq.agent.TEMPLATES.Backstory import BACKSTORY
304
+ backstory = BACKSTORY.format(
305
+ role=self.role,
306
+ knowledge=self.knowledge if isinstance(self.knowledge, str) else None,
307
+ skillsets=", ".join([item for item in self.skillsets]),
308
+ rag_tool_overview=", ".join([item.name for item in self.tools]),
309
+ goal=self.goal,
310
+ )
311
+ self.backstory = backstory
312
+
313
+ return self
314
+
315
+
374
316
  def invoke(
375
- self,
376
- prompts: str,
377
- output_formats: List[TaskOutputFormat],
378
- response_fields: List[ResponseField],
379
- **kwargs,
380
- ) -> Dict[str, Any]:
317
+ self, prompts: str, output_formats: List[TaskOutputFormat], response_fields: List[ResponseField], **kwargs
318
+ ) -> Dict[str, Any]:
381
319
  """
382
320
  Receive the system prompt in string and create formatted prompts using the system prompt and the agent's backstory.
383
321
  Then call the base model.
@@ -402,9 +340,7 @@ class Agent(ABC, BaseModel):
402
340
  task_execution_counter += 1
403
341
  print("Agent's #1 res: ", response)
404
342
 
405
- if (
406
- response is None or response == ""
407
- ) and task_execution_counter < self.max_retry_limit:
343
+ if (response is None or response == "") and task_execution_counter < self.max_retry_limit:
408
344
  while task_execution_counter <= self.max_retry_limit:
409
345
  response = self.llm.call(
410
346
  messages=messages,
@@ -421,6 +357,7 @@ class Agent(ABC, BaseModel):
421
357
 
422
358
  return {"output": response.output if hasattr(response, "output") else response}
423
359
 
360
+
424
361
  def execute_task(self, task, context: Optional[str] = None) -> str:
425
362
  """
426
363
  Execute the task and return the output in string.
@@ -430,8 +367,8 @@ class Agent(ABC, BaseModel):
430
367
  """
431
368
 
432
369
  task_prompt = task.prompt()
433
- # if context:
434
- # task_prompt = self.i18n.slice("task_with_context").format(task=task_prompt, context=context)
370
+ if context:
371
+ task_prompt += context
435
372
 
436
373
  tool_results = []
437
374
  if task.tools_called: