mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-09 11:51:37 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
000bdf3418 | ||
|
|
812abec2eb | ||
|
|
e58f6deb4b | ||
|
|
f23680c121 | ||
|
|
e569400260 | ||
|
|
6249d14d3f | ||
|
|
fb4ecf1893 | ||
|
|
5a4cbf8aeb | ||
|
|
885a73f697 | ||
|
|
6d55cfa87e | ||
|
|
e858cebed9 | ||
|
|
4ffbc1b52a | ||
|
|
2ef506fa8a | ||
|
|
7ce783e59b | ||
|
|
35250e429e |
No files matched your search
@@ -75,17 +75,29 @@ jobs:
|
||||
issue_number: issueNumber
|
||||
});
|
||||
|
||||
const existingComment = comments.data.find(comment =>
|
||||
// Look for Claude's completed review comments
|
||||
const claudeReviewComments = comments.data.filter(comment =>
|
||||
(comment.user.login === 'claude[bot]' || comment.body.includes('✅') || comment.body.includes('## Summary')) &&
|
||||
!comment.body.includes('⏳') &&
|
||||
!comment.body.includes('🔄') &&
|
||||
!comment.body.includes('Loading...')
|
||||
);
|
||||
|
||||
// Check if our review request comment already exists
|
||||
const existingReviewRequest = comments.data.find(comment =>
|
||||
comment.body.includes(reviewText)
|
||||
);
|
||||
|
||||
const shouldPost = !existingComment;
|
||||
// Post if Claude has completed a review AND we haven't posted the review request yet
|
||||
const shouldPost = claudeReviewComments.length > 0 && !existingReviewRequest;
|
||||
core.setOutput('should-post', shouldPost);
|
||||
console.log(`Claude review completed: ${claudeReviewComments.length > 0}`);
|
||||
console.log(`Review request exists: ${!!existingReviewRequest}`);
|
||||
console.log(`Should post comment: ${shouldPost}`);
|
||||
|
||||
} catch (error) {
|
||||
console.log('Error checking comments:', error);
|
||||
core.setOutput('should-post', true); // If we can't check, proceed with posting
|
||||
core.setOutput('should-post', false); // If we can't check, don't post
|
||||
}
|
||||
|
||||
- name: Wait 30 seconds before posting comment
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
flask \
|
||||
"praisonai>=2.2.56" \
|
||||
"praisonai>=2.2.57" \
|
||||
"praisonai[api]" \
|
||||
gunicorn \
|
||||
markdown
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.56" \
|
||||
"praisonai>=2.2.57" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.56" \
|
||||
"praisonai>=2.2.57" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.2.56" \
|
||||
"praisonai>=2.2.57" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
+2
-2
@@ -121,7 +121,7 @@ healthcheck:
|
||||
## 📦 Package Versions
|
||||
|
||||
All Docker images use consistent, up-to-date versions:
|
||||
- PraisonAI: `>=2.2.56`
|
||||
- PraisonAI: `>=2.2.57`
|
||||
- PraisonAI Agents: `>=0.0.92`
|
||||
- Python: `3.11-slim`
|
||||
|
||||
@@ -218,7 +218,7 @@ docker-compose up -d
|
||||
### Version Pinning
|
||||
To use specific versions, update the Dockerfile:
|
||||
```dockerfile
|
||||
RUN pip install "praisonai==2.2.56" "praisonaiagents==0.0.92"
|
||||
RUN pip install "praisonai==2.2.57" "praisonaiagents==0.0.92"
|
||||
```
|
||||
|
||||
## 🌐 Production Deployment
|
||||
|
||||
@@ -1100,6 +1100,10 @@ Your Goal: {self.goal}"""
|
||||
execute_tool_fn=self.execute_tool,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id,
|
||||
reasoning_steps=reasoning_steps
|
||||
)
|
||||
else:
|
||||
@@ -1116,6 +1120,10 @@ Your Goal: {self.goal}"""
|
||||
execute_tool_fn=self.execute_tool,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id,
|
||||
reasoning_steps=reasoning_steps
|
||||
)
|
||||
else:
|
||||
@@ -1162,14 +1170,26 @@ Your Goal: {self.goal}"""
|
||||
message=prompt,
|
||||
response=response,
|
||||
markdown=self.markdown,
|
||||
generation_time=generation_time
|
||||
generation_time=generation_time,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
|
||||
task_name=None, # Not available in this context
|
||||
task_description=None, # Not available in this context
|
||||
task_id=None # Not available in this context
|
||||
)
|
||||
# Only display interaction if not using custom LLM (to avoid double output) and verbose is True
|
||||
if self.verbose and not self._using_custom_llm:
|
||||
display_interaction(prompt, response, markdown=self.markdown,
|
||||
generation_time=generation_time, console=self.console)
|
||||
generation_time=generation_time, console=self.console,
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
|
||||
task_name=None, # Not available in this context
|
||||
task_description=None, # Not available in this context
|
||||
task_id=None) # Not available in this context
|
||||
|
||||
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, stream=True):
|
||||
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, stream=True, task_name=None, task_description=None, task_id=None):
|
||||
# Log all parameter values when in debug mode
|
||||
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
|
||||
param_info = {
|
||||
@@ -1260,6 +1280,9 @@ Your Goal: {self.goal}"""
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)],
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id,
|
||||
execute_tool_fn=self.execute_tool, # Pass tool execution function
|
||||
reasoning_steps=reasoning_steps,
|
||||
stream=stream # Pass the stream parameter from chat method
|
||||
@@ -1582,6 +1605,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
agent_name=self.name,
|
||||
agent_role=self.role,
|
||||
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)],
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id,
|
||||
execute_tool_fn=self.execute_tool_async,
|
||||
reasoning_steps=reasoning_steps
|
||||
)
|
||||
|
||||
@@ -144,8 +144,17 @@ class ImageAgent(Agent):
|
||||
config = self.image_config.dict(exclude_none=True)
|
||||
config.update(kwargs)
|
||||
|
||||
# Use llm parameter as the model
|
||||
config['model'] = self.llm
|
||||
# Get the model name robustly from the parent Agent's property
|
||||
model_info = self.llm_model
|
||||
model_name = model_info.model if hasattr(model_info, 'model') else str(model_info)
|
||||
|
||||
# Use the model name in config
|
||||
config['model'] = model_name
|
||||
|
||||
# Check if we're using a Gemini model and remove unsupported parameters
|
||||
if 'gemini' in model_name.lower():
|
||||
# Gemini models don't support response_format parameter
|
||||
config.pop('response_format', None)
|
||||
|
||||
with Progress(
|
||||
SpinnerColumn(),
|
||||
@@ -154,7 +163,7 @@ class ImageAgent(Agent):
|
||||
) as progress:
|
||||
try:
|
||||
# Add a task for image generation
|
||||
task = progress.add_task(f"[cyan]Generating image with {self.llm}...", total=None)
|
||||
task = progress.add_task(f"[cyan]Generating image with {model_name}...", total=None)
|
||||
|
||||
# Use litellm's image generation
|
||||
response = self.litellm(
|
||||
|
||||
@@ -689,7 +689,10 @@ Context:
|
||||
_get_multimodal_message(task_prompt, task.images),
|
||||
tools=task.tools,
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic
|
||||
output_pydantic=task.output_pydantic,
|
||||
task_name=task.name,
|
||||
task_description=task.description,
|
||||
task_id=task_id
|
||||
)
|
||||
else:
|
||||
agent_output = executor_agent.chat(
|
||||
@@ -698,6 +701,9 @@ Context:
|
||||
output_json=task.output_json,
|
||||
output_pydantic=task.output_pydantic,
|
||||
stream=self.stream,
|
||||
task_name=task.name,
|
||||
task_description=task.description,
|
||||
task_id=task_id
|
||||
)
|
||||
|
||||
if agent_output:
|
||||
|
||||
@@ -620,6 +620,9 @@ class LLM:
|
||||
agent_name: Optional[str] = None,
|
||||
agent_role: Optional[str] = None,
|
||||
agent_tools: Optional[List[str]] = None,
|
||||
task_name: Optional[str] = None,
|
||||
task_description: Optional[str] = None,
|
||||
task_id: Optional[str] = None,
|
||||
execute_tool_fn: Optional[Callable] = None,
|
||||
stream: bool = True,
|
||||
**kwargs
|
||||
@@ -760,8 +763,14 @@ class LLM:
|
||||
original_prompt,
|
||||
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
|
||||
markdown=markdown,
|
||||
generation_time=generation_time_val,
|
||||
console=console
|
||||
generation_time=time.time() - current_time,
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
elif verbose and not interaction_displayed:
|
||||
@@ -769,8 +778,14 @@ class LLM:
|
||||
original_prompt,
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=generation_time_val,
|
||||
console=console
|
||||
generation_time=time.time() - current_time,
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
|
||||
@@ -883,7 +898,13 @@ class LLM:
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - current_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
|
||||
@@ -963,16 +984,14 @@ class LLM:
|
||||
iteration_count += 1
|
||||
continue
|
||||
|
||||
# For Ollama, add explicit prompt if we need a final answer
|
||||
if self._is_ollama_provider() and iteration_count > 0:
|
||||
# Add an explicit prompt for Ollama to generate the final answer
|
||||
messages.append({
|
||||
"role": "user",
|
||||
"content": self.OLLAMA_FINAL_ANSWER_PROMPT
|
||||
})
|
||||
# Check if the LLM provided a final answer alongside the tool calls
|
||||
# If response_text contains substantive content, treat it as the final answer
|
||||
if response_text and response_text.strip() and len(response_text.strip()) > 10:
|
||||
# LLM provided a final answer after tool execution, don't continue
|
||||
final_response_text = response_text.strip()
|
||||
break
|
||||
|
||||
# After tool execution, continue the loop to check if more tools are needed
|
||||
# instead of immediately trying to get a final response
|
||||
# Otherwise, continue the loop to check if more tools are needed
|
||||
iteration_count += 1
|
||||
continue
|
||||
else:
|
||||
@@ -1010,7 +1029,13 @@ class LLM:
|
||||
f"Reasoning:\n{stored_reasoning_content}\n\nAnswer:\n{response_text}",
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
else:
|
||||
display_interaction(
|
||||
@@ -1018,7 +1043,13 @@ class LLM:
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
|
||||
@@ -1043,7 +1074,9 @@ class LLM:
|
||||
)
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(original_prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
|
||||
@@ -1060,7 +1093,9 @@ class LLM:
|
||||
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(original_prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
# Return reasoning content if reasoning_steps is True
|
||||
if reasoning_steps and stored_reasoning_content:
|
||||
@@ -1106,7 +1141,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
elif verbose:
|
||||
display_interaction(
|
||||
@@ -1114,7 +1155,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
reflection_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
else:
|
||||
# Existing streaming approach
|
||||
@@ -1165,14 +1212,18 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
if satisfactory and reflection_count >= min_reflect - 1:
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
|
||||
if reflection_count >= max_reflect - 1:
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
|
||||
@@ -1193,6 +1244,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
messages=messages,
|
||||
temperature=temperature,
|
||||
stream=True,
|
||||
tools=formatted_tools,
|
||||
output_json=output_json,
|
||||
output_pydantic=output_pydantic,
|
||||
**kwargs
|
||||
@@ -1209,6 +1261,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
messages=messages,
|
||||
temperature=temperature,
|
||||
stream=True,
|
||||
tools=formatted_tools,
|
||||
output_json=output_json,
|
||||
output_pydantic=output_pydantic,
|
||||
**kwargs
|
||||
@@ -1225,7 +1278,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
if reflection_count >= max_reflect:
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
continue
|
||||
@@ -1273,6 +1328,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
agent_name: Optional[str] = None,
|
||||
agent_role: Optional[str] = None,
|
||||
agent_tools: Optional[List[str]] = None,
|
||||
task_name: Optional[str] = None,
|
||||
task_description: Optional[str] = None,
|
||||
task_id: Optional[str] = None,
|
||||
execute_tool_fn: Optional[Callable] = None,
|
||||
stream: bool = True,
|
||||
**kwargs
|
||||
@@ -1380,7 +1438,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
elif verbose and not interaction_displayed:
|
||||
@@ -1389,7 +1453,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
else:
|
||||
@@ -1473,7 +1543,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
|
||||
@@ -1567,7 +1643,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
elif verbose and not interaction_displayed:
|
||||
@@ -1576,7 +1658,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
interaction_displayed = True
|
||||
else:
|
||||
@@ -1626,6 +1714,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
if reasoning_steps and reasoning_content:
|
||||
stored_reasoning_content = reasoning_content
|
||||
|
||||
# Check if the LLM provided a final answer alongside the tool calls
|
||||
# If response_text contains substantive content, treat it as the final answer
|
||||
if response_text and response_text.strip() and len(response_text.strip()) > 10:
|
||||
# LLM provided a final answer after tool execution, don't continue
|
||||
final_response_text = response_text.strip()
|
||||
break
|
||||
|
||||
# Continue the loop to check if more tools are needed
|
||||
iteration_count += 1
|
||||
continue
|
||||
@@ -1642,7 +1737,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
self.chat_history.append({"role": "assistant", "content": response_text})
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(original_prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
|
||||
@@ -1658,11 +1755,19 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
f"Reasoning:\n{stored_reasoning_content}\n\nAnswer:\n{display_text}",
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
else:
|
||||
display_interaction(original_prompt, display_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
|
||||
# Return reasoning content if reasoning_steps is True and we have it
|
||||
@@ -1707,7 +1812,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
elif verbose:
|
||||
display_interaction(
|
||||
@@ -1715,7 +1826,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
reflection_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console
|
||||
console=console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
else:
|
||||
# Existing streaming approach
|
||||
@@ -1767,14 +1884,18 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
if satisfactory and reflection_count >= min_reflect - 1:
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
|
||||
if reflection_count >= max_reflect - 1:
|
||||
if verbose and not interaction_displayed:
|
||||
display_interaction(prompt, response_text, markdown=markdown,
|
||||
generation_time=time.time() - start_time, console=console)
|
||||
generation_time=time.time() - start_time, console=console,
|
||||
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
|
||||
task_name=task_name, task_description=task_description, task_id=task_id)
|
||||
interaction_displayed = True
|
||||
return response_text
|
||||
|
||||
@@ -2039,6 +2160,12 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
verbose: bool = True,
|
||||
markdown: bool = True,
|
||||
console: Optional[Console] = None,
|
||||
agent_name: Optional[str] = None,
|
||||
agent_role: Optional[str] = None,
|
||||
agent_tools: Optional[List[str]] = None,
|
||||
task_name: Optional[str] = None,
|
||||
task_description: Optional[str] = None,
|
||||
task_id: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> str:
|
||||
"""Simple function to get model response without tool calls or complex features"""
|
||||
@@ -2107,7 +2234,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console or self.console
|
||||
console=console or self.console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
|
||||
return response_text.strip() if response_text else ""
|
||||
@@ -2126,6 +2259,12 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
verbose: bool = True,
|
||||
markdown: bool = True,
|
||||
console: Optional[Console] = None,
|
||||
agent_name: Optional[str] = None,
|
||||
agent_role: Optional[str] = None,
|
||||
agent_tools: Optional[List[str]] = None,
|
||||
task_name: Optional[str] = None,
|
||||
task_description: Optional[str] = None,
|
||||
task_id: Optional[str] = None,
|
||||
**kwargs
|
||||
) -> str:
|
||||
"""Async version of response function"""
|
||||
@@ -2195,7 +2334,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
||||
response_text,
|
||||
markdown=markdown,
|
||||
generation_time=time.time() - start_time,
|
||||
console=console or self.console
|
||||
console=console or self.console,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
|
||||
return response_text.strip() if response_text else ""
|
||||
|
||||
@@ -73,7 +73,18 @@ def execute_sync_callback(display_type: str, **kwargs):
|
||||
"""
|
||||
if display_type in sync_display_callbacks:
|
||||
callback = sync_display_callbacks[display_type]
|
||||
callback(**kwargs)
|
||||
import inspect
|
||||
sig = inspect.signature(callback)
|
||||
|
||||
# Filter kwargs to what the callback accepts to maintain backward compatibility
|
||||
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
|
||||
# Callback accepts **kwargs, so pass all arguments
|
||||
supported_kwargs = kwargs
|
||||
else:
|
||||
# Only pass arguments that the callback signature supports
|
||||
supported_kwargs = {k: v for k, v in kwargs.items() if k in sig.parameters}
|
||||
|
||||
callback(**supported_kwargs)
|
||||
|
||||
async def execute_callback(display_type: str, **kwargs):
|
||||
"""Execute both sync and async callbacks for a given display type.
|
||||
@@ -82,16 +93,38 @@ async def execute_callback(display_type: str, **kwargs):
|
||||
display_type (str): Type of display event
|
||||
**kwargs: Arguments to pass to the callback functions
|
||||
"""
|
||||
import inspect
|
||||
|
||||
# Execute synchronous callback if registered
|
||||
if display_type in sync_display_callbacks:
|
||||
callback = sync_display_callbacks[display_type]
|
||||
sig = inspect.signature(callback)
|
||||
|
||||
# Filter kwargs to what the callback accepts to maintain backward compatibility
|
||||
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
|
||||
# Callback accepts **kwargs, so pass all arguments
|
||||
supported_kwargs = kwargs
|
||||
else:
|
||||
# Only pass arguments that the callback signature supports
|
||||
supported_kwargs = {k: v for k, v in kwargs.items() if k in sig.parameters}
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
await loop.run_in_executor(None, lambda: callback(**kwargs))
|
||||
await loop.run_in_executor(None, lambda: callback(**supported_kwargs))
|
||||
|
||||
# Execute asynchronous callback if registered
|
||||
if display_type in async_display_callbacks:
|
||||
callback = async_display_callbacks[display_type]
|
||||
await callback(**kwargs)
|
||||
sig = inspect.signature(callback)
|
||||
|
||||
# Filter kwargs to what the callback accepts to maintain backward compatibility
|
||||
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
|
||||
# Callback accepts **kwargs, so pass all arguments
|
||||
supported_kwargs = kwargs
|
||||
else:
|
||||
# Only pass arguments that the callback signature supports
|
||||
supported_kwargs = {k: v for k, v in kwargs.items() if k in sig.parameters}
|
||||
|
||||
await callback(**supported_kwargs)
|
||||
|
||||
def _clean_display_content(content: str, max_length: int = 20000) -> str:
|
||||
"""Helper function to clean and truncate content for display."""
|
||||
@@ -114,7 +147,7 @@ def _clean_display_content(content: str, max_length: int = 20000) -> str:
|
||||
|
||||
return content.strip()
|
||||
|
||||
def display_interaction(message, response, markdown=True, generation_time=None, console=None):
|
||||
def display_interaction(message, response, markdown=True, generation_time=None, console=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None):
|
||||
"""Synchronous version of display_interaction."""
|
||||
if console is None:
|
||||
console = Console()
|
||||
@@ -126,6 +159,35 @@ def display_interaction(message, response, markdown=True, generation_time=None,
|
||||
message = _clean_display_content(str(message))
|
||||
response = _clean_display_content(str(response))
|
||||
|
||||
|
||||
# Execute synchronous callback if registered
|
||||
if 'interaction' in sync_display_callbacks:
|
||||
callback = sync_display_callbacks['interaction']
|
||||
import inspect
|
||||
sig = inspect.signature(callback)
|
||||
|
||||
all_kwargs = {
|
||||
'message': message,
|
||||
'response': response,
|
||||
'markdown': markdown,
|
||||
'generation_time': generation_time,
|
||||
'agent_name': agent_name,
|
||||
'agent_role': agent_role,
|
||||
'agent_tools': agent_tools,
|
||||
'task_name': task_name,
|
||||
'task_description': task_description,
|
||||
'task_id': task_id
|
||||
}
|
||||
|
||||
# Filter kwargs to what the callback accepts to maintain backward compatibility
|
||||
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
|
||||
# Callback accepts **kwargs, so pass all arguments
|
||||
supported_kwargs = all_kwargs
|
||||
else:
|
||||
# Only pass arguments that the callback signature supports
|
||||
supported_kwargs = {k: v for k, v in all_kwargs.items() if k in sig.parameters}
|
||||
|
||||
callback(**supported_kwargs)
|
||||
# Rest of the display logic...
|
||||
if generation_time:
|
||||
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
|
||||
@@ -204,7 +266,7 @@ def display_generating(content: str = "", start_time: Optional[float] = None):
|
||||
return Panel(Markdown(content), title=f"Generating...{elapsed_str}", border_style="green")
|
||||
|
||||
# Async versions with 'a' prefix
|
||||
async def adisplay_interaction(message, response, markdown=True, generation_time=None, console=None):
|
||||
async def adisplay_interaction(message, response, markdown=True, generation_time=None, console=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None):
|
||||
"""Async version of display_interaction."""
|
||||
if console is None:
|
||||
console = Console()
|
||||
@@ -222,7 +284,13 @@ async def adisplay_interaction(message, response, markdown=True, generation_time
|
||||
message=message,
|
||||
response=response,
|
||||
markdown=markdown,
|
||||
generation_time=generation_time
|
||||
generation_time=generation_time,
|
||||
agent_name=agent_name,
|
||||
agent_role=agent_role,
|
||||
agent_tools=agent_tools,
|
||||
task_name=task_name,
|
||||
task_description=task_description,
|
||||
task_id=task_id
|
||||
)
|
||||
|
||||
# Rest of the display logic...
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.130"
|
||||
version = "0.0.131"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test script for callback enhancement - Issue #896"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
import asyncio
|
||||
from praisonaiagents import (
|
||||
register_display_callback,
|
||||
Agent,
|
||||
Task,
|
||||
PraisonAIAgents
|
||||
)
|
||||
|
||||
def enhanced_callback(message=None, response=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None, **kwargs):
|
||||
"""Enhanced callback that demonstrates the new context information"""
|
||||
print("="*80)
|
||||
print("ENHANCED CALLBACK TRIGGERED")
|
||||
print("="*80)
|
||||
print(f"Message: {message}")
|
||||
print(f"Response: {response}")
|
||||
print(f"Agent Name: {agent_name}")
|
||||
print(f"Agent Role: {agent_role}")
|
||||
print(f"Agent Tools: {agent_tools}")
|
||||
print(f"Task Name: {task_name}")
|
||||
print(f"Task Description: {task_description}")
|
||||
print(f"Task ID: {task_id}")
|
||||
print(f"Other kwargs: {kwargs}")
|
||||
print("="*80)
|
||||
print()
|
||||
|
||||
# Register the enhanced callback
|
||||
register_display_callback('interaction', enhanced_callback, is_async=False)
|
||||
|
||||
def test_callback_enhancement():
|
||||
"""Test function to verify the callback enhancement works"""
|
||||
|
||||
# Create an agent with detailed information
|
||||
agent = Agent(
|
||||
name="TestAgent",
|
||||
role="Assistant",
|
||||
goal="Help with testing callbacks",
|
||||
backstory="I am a helpful assistant for testing purposes",
|
||||
llm="openai/gpt-4o-mini",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Create a task with detailed information
|
||||
task = Task(
|
||||
name="callback_test_task",
|
||||
description="Test the enhanced callback functionality",
|
||||
agent=agent,
|
||||
expected_output="A simple response to test callbacks"
|
||||
)
|
||||
|
||||
# Run the agents
|
||||
try:
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
verbose=True
|
||||
)
|
||||
result = agents.start()
|
||||
print(f"Test completed successfully! Result: {result}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Test failed with error: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing callback enhancement (Issue #896)...")
|
||||
print("This test will demonstrate the new agent_name and task context in callbacks")
|
||||
print()
|
||||
|
||||
# Run the test
|
||||
success = test_callback_enhancement()
|
||||
|
||||
if success:
|
||||
print("✅ Test passed! Callback enhancement is working correctly.")
|
||||
else:
|
||||
print("❌ Test failed! Check the error output above.")
|
||||
@@ -34,7 +34,7 @@ agent = Agent(
|
||||
role="Assistant",
|
||||
goal="Help with tasks",
|
||||
backstory="I am a helpful assistant",
|
||||
llm="openai/gpt-4o-mini", # Using a simple model for testing
|
||||
llm="gpt-4o-mini", # Using a simple model for testing
|
||||
verbose=False
|
||||
)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Simple test for callback enhancement - Issue #896"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
# Test just the callback registration and display functions
|
||||
from praisonaiagents import register_display_callback, display_interaction
|
||||
|
||||
def test_callback(message=None, response=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None, **kwargs):
|
||||
"""Test callback function"""
|
||||
print("Callback triggered with:")
|
||||
print(f" Message: {message}")
|
||||
print(f" Response: {response}")
|
||||
print(f" Agent Name: {agent_name}")
|
||||
print(f" Agent Role: {agent_role}")
|
||||
print(f" Agent Tools: {agent_tools}")
|
||||
print(f" Task Name: {task_name}")
|
||||
print(f" Task Description: {task_description}")
|
||||
print(f" Task ID: {task_id}")
|
||||
print(f" Other kwargs: {kwargs}")
|
||||
print()
|
||||
|
||||
# Register the callback
|
||||
register_display_callback('interaction', test_callback, is_async=False)
|
||||
|
||||
# Test the display_interaction function with new parameters
|
||||
print("Testing display_interaction with enhanced parameters...")
|
||||
|
||||
display_interaction(
|
||||
message="Test message",
|
||||
response="Test response",
|
||||
agent_name="TestAgent",
|
||||
agent_role="Assistant",
|
||||
agent_tools=["tool1", "tool2"],
|
||||
task_name="test_task",
|
||||
task_description="Test task description",
|
||||
task_id="task_123",
|
||||
markdown=True,
|
||||
generation_time=1.5
|
||||
)
|
||||
|
||||
print("✅ Simple callback test completed!")
|
||||
File renamed without changes.
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Simple test to verify the self-reflection fix works"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
def test_self_reflection_fix():
|
||||
"""Test that self-reflection works with tools after the fix"""
|
||||
print("=== Testing Self-Reflection Fix ===")
|
||||
|
||||
# Create an agent with self-reflection and a simple tool
|
||||
agent = Agent(
|
||||
role="Math Assistant",
|
||||
goal="Solve math problems accurately",
|
||||
backstory="You are a helpful math assistant",
|
||||
self_reflect=True,
|
||||
llm="gpt-4o-mini", # Use a more widely available model
|
||||
verbose=True,
|
||||
tools=[calculator],
|
||||
min_reflect=1,
|
||||
max_reflect=2
|
||||
)
|
||||
|
||||
# Test with a simple calculation that might trigger self-reflection
|
||||
response = agent.start("What is 25 * 17? Show your work and double-check the answer.")
|
||||
print(f"\nResponse: {response}")
|
||||
|
||||
if response:
|
||||
print("\n✅ SUCCESS: Self-reflection with tools is working!")
|
||||
return True
|
||||
else:
|
||||
print("\n❌ FAILED: Self-reflection with tools is not working")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_self_reflection_fix()
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test to verify the self-reflection fix works with tools"""
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
def test_self_reflection_fix_verification():
|
||||
"""Test that self-reflection now works with tools"""
|
||||
print("=== Testing Self-Reflection Fix Verification ===")
|
||||
|
||||
# Create an agent with self-reflection enabled and tools
|
||||
agent = Agent(
|
||||
name="MathAgent",
|
||||
role="Math Assistant",
|
||||
goal="Solve mathematical problems accurately",
|
||||
backstory="You are a helpful math assistant who double-checks calculations",
|
||||
tools=[calculator],
|
||||
self_reflect=True, # This should now work with tools
|
||||
min_reflect=1,
|
||||
max_reflect=2,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Define a task that would benefit from self-reflection
|
||||
task = Task(
|
||||
description="Calculate 123 * 456 and verify the result is correct",
|
||||
expected_output="The calculation result with verification",
|
||||
agent=agent,
|
||||
name="math_calculation"
|
||||
)
|
||||
|
||||
# Create and run the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
try:
|
||||
result = agents.start()
|
||||
|
||||
if result:
|
||||
print(f"\n✅ SUCCESS: Self-reflection with tools is working!")
|
||||
print(f"Result: {result}")
|
||||
return True
|
||||
else:
|
||||
print(f"\n❌ FAILED: No result returned")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {str(e)}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_self_reflection_fix_verification()
|
||||
|
||||
if success:
|
||||
print("\n🎉 Fix verification successful! Self-reflection now works with tools.")
|
||||
else:
|
||||
print("\n💥 Fix verification failed. Self-reflection with tools is still not working.")
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Demonstration of Issue #896 Enhancement: Callback Needs More Contextual Information
|
||||
|
||||
This script demonstrates how the enhanced callback system now provides agent_name,
|
||||
agent_role, agent_tools, task_name, task_description, and task_id to callbacks.
|
||||
|
||||
Usage:
|
||||
python test_issue_896_demo.py
|
||||
|
||||
Author: Generated by Claude Code
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
|
||||
|
||||
from praisonaiagents import register_display_callback, display_interaction
|
||||
|
||||
def enhanced_callback(message=None, response=None, agent_name=None, agent_role=None,
|
||||
agent_tools=None, task_name=None, task_description=None,
|
||||
task_id=None, **kwargs):
|
||||
"""
|
||||
Enhanced callback function that demonstrates the new contextual information.
|
||||
|
||||
This callback now receives:
|
||||
- agent_name: The name of the agent
|
||||
- agent_role: The role of the agent
|
||||
- agent_tools: List of tools available to the agent
|
||||
- task_name: The name of the task being executed
|
||||
- task_description: The description of the task
|
||||
- task_id: The unique identifier of the task
|
||||
"""
|
||||
print("="*80)
|
||||
print("🔔 ENHANCED CALLBACK TRIGGERED")
|
||||
print("="*80)
|
||||
print(f"📝 Message: {message}")
|
||||
print(f"💬 Response: {response}")
|
||||
print(f"🤖 Agent Name: {agent_name}")
|
||||
print(f"👤 Agent Role: {agent_role}")
|
||||
print(f"🛠️ Agent Tools: {agent_tools}")
|
||||
print(f"📋 Task Name: {task_name}")
|
||||
print(f"📄 Task Description: {task_description}")
|
||||
print(f"🔢 Task ID: {task_id}")
|
||||
print(f"⚙️ Other kwargs: {kwargs}")
|
||||
print("="*80)
|
||||
print()
|
||||
|
||||
def legacy_callback(message=None, response=None, **kwargs):
|
||||
"""
|
||||
Legacy callback function that demonstrates backward compatibility.
|
||||
|
||||
This callback works exactly as before, proving that existing callbacks
|
||||
continue to function without modification.
|
||||
"""
|
||||
print("🔄 Legacy callback still works!")
|
||||
print(f"Message: {message}")
|
||||
print(f"Response: {response}")
|
||||
print(f"Other kwargs: {kwargs}")
|
||||
print()
|
||||
|
||||
def demo_enhanced_callbacks():
|
||||
"""Demonstrate the enhanced callback functionality"""
|
||||
|
||||
print("🚀 Demonstrating Enhanced Callbacks for Issue #896")
|
||||
print("="*60)
|
||||
print()
|
||||
|
||||
# Register both enhanced and legacy callbacks
|
||||
register_display_callback('interaction', enhanced_callback, is_async=False)
|
||||
|
||||
print("1. Testing enhanced callback with full context:")
|
||||
print("-" * 50)
|
||||
|
||||
# Test with full context
|
||||
display_interaction(
|
||||
message="You need to do the following task: Say the number 1.\nExpected Output: 1.\nPlease provide only the final result of your work.",
|
||||
response="1",
|
||||
agent_name="MyAgent",
|
||||
agent_role="Assistant",
|
||||
agent_tools=["calculator", "text_processor"],
|
||||
task_name="simple_task",
|
||||
task_description="Say the number 1",
|
||||
task_id="task_001",
|
||||
markdown=True,
|
||||
generation_time=1.3638858795166016
|
||||
)
|
||||
|
||||
print("2. Testing backward compatibility (missing context):")
|
||||
print("-" * 50)
|
||||
|
||||
# Test with minimal context (backward compatibility)
|
||||
display_interaction(
|
||||
message="Test message without context",
|
||||
response="Test response",
|
||||
markdown=True,
|
||||
generation_time=0.5
|
||||
)
|
||||
|
||||
print("3. Testing with partial context:")
|
||||
print("-" * 50)
|
||||
|
||||
# Test with partial context
|
||||
display_interaction(
|
||||
message="Partial context test",
|
||||
response="Response with partial context",
|
||||
agent_name="PartialAgent",
|
||||
task_name="partial_task",
|
||||
markdown=True,
|
||||
generation_time=0.8
|
||||
)
|
||||
|
||||
print("✅ All tests completed successfully!")
|
||||
print()
|
||||
print("🎉 The enhanced callback system is working correctly!")
|
||||
print(" - Callbacks now receive agent and task context")
|
||||
print(" - Backward compatibility is maintained")
|
||||
print(" - All parameters are optional with sensible defaults")
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo_enhanced_callbacks()
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test LLM class directly to verify self-reflection fix"""
|
||||
|
||||
from praisonaiagents.llm import LLM
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
def test_llm_direct():
|
||||
"""Test LLM class directly with self-reflection and tools"""
|
||||
print("=== Testing LLM Direct with Self-Reflection and Tools ===")
|
||||
|
||||
# Create LLM instance
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
|
||||
# Test with self-reflection and tools
|
||||
response = llm.get_response(
|
||||
prompt="Calculate 15 * 23 and verify your answer",
|
||||
system_prompt="You are a helpful math assistant. Use tools when needed.",
|
||||
tools=[calculator],
|
||||
self_reflect=True,
|
||||
min_reflect=1,
|
||||
max_reflect=2,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
print(f"\nResponse: {response}")
|
||||
|
||||
if response:
|
||||
print("\n✅ SUCCESS: LLM self-reflection with tools is working!")
|
||||
return True
|
||||
else:
|
||||
print("\n❌ FAILED: LLM self-reflection with tools is not working")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_llm_direct()
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test script to verify self-reflection works with tools after the fix"""
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import duckduckgo_search
|
||||
|
||||
def test_self_reflection_with_tools():
|
||||
"""Test self-reflection with tools - should work after the fix"""
|
||||
print("=== Testing Self-Reflection WITH Tools ===")
|
||||
|
||||
# Create an agent with self-reflection and tools
|
||||
agent = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Analyze and provide insights on given topics",
|
||||
backstory="You are an expert analyst with strong critical thinking skills",
|
||||
self_reflect=True,
|
||||
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
|
||||
verbose=True,
|
||||
tools=[duckduckgo_search]
|
||||
)
|
||||
|
||||
# Create a task
|
||||
task = Task(
|
||||
description="Search for recent developments in AI and provide a brief analysis",
|
||||
expected_output="A detailed analysis report",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Create and start the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
print(f"Result with tools: {result}")
|
||||
return result
|
||||
|
||||
def test_self_reflection_without_tools():
|
||||
"""Test self-reflection without tools - should work (baseline)"""
|
||||
print("\n=== Testing Self-Reflection WITHOUT Tools ===")
|
||||
|
||||
# Create an agent with self-reflection but no tools
|
||||
agent = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Analyze and provide insights on given topics",
|
||||
backstory="You are an expert analyst with strong critical thinking skills",
|
||||
self_reflect=True,
|
||||
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Create a task
|
||||
task = Task(
|
||||
description="Analyze recent developments in AI",
|
||||
expected_output="A detailed analysis report",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Create and start the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
print(f"Result without tools: {result}")
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing self-reflection fix...")
|
||||
|
||||
# Test without tools (should work)
|
||||
result_without_tools = test_self_reflection_without_tools()
|
||||
|
||||
# Test with tools (should work after fix)
|
||||
result_with_tools = test_self_reflection_with_tools()
|
||||
|
||||
print("\n=== Test Summary ===")
|
||||
print(f"Without tools: {'SUCCESS' if result_without_tools else 'FAILED'}")
|
||||
print(f"With tools: {'SUCCESS' if result_with_tools else 'FAILED'}")
|
||||
|
||||
if result_with_tools:
|
||||
print("\n✅ Fix verified: Self-reflection now works with tools!")
|
||||
else:
|
||||
print("\n❌ Fix failed: Self-reflection still not working with tools")
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test LLM class directly to verify self-reflection fix"""
|
||||
|
||||
from praisonaiagents.llm import LLM
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
def test_llm_direct():
|
||||
"""Test LLM class directly with self-reflection and tools"""
|
||||
print("=== Testing LLM Direct with Self-Reflection and Tools ===")
|
||||
|
||||
# Create LLM instance
|
||||
llm = LLM(model="gpt-4o-mini")
|
||||
|
||||
# Test with self-reflection and tools
|
||||
try:
|
||||
response = llm.get_response(
|
||||
prompt="Calculate 15 * 23 and verify your answer",
|
||||
system_prompt="You are a helpful math assistant. Use tools when needed.",
|
||||
tools=[calculator],
|
||||
self_reflect=True,
|
||||
min_reflect=1,
|
||||
max_reflect=2,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
print(f"\nResponse: {response}")
|
||||
|
||||
assert response, "LLM self-reflection with tools failed to produce a response."
|
||||
print("\n✅ SUCCESS: LLM self-reflection with tools is working!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {str(e)}")
|
||||
raise AssertionError(f"Test failed with error: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_llm_direct()
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Comprehensive test script to verify self-reflection works with tools after the fix"""
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import duckduckgo_search
|
||||
|
||||
def test_self_reflection_with_tools():
|
||||
"""Test self-reflection with tools - should work after the fix"""
|
||||
print("=== Testing Self-Reflection WITH Tools ===")
|
||||
|
||||
# Create an agent with self-reflection and tools
|
||||
agent = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Analyze and provide insights on given topics",
|
||||
backstory="You are an expert analyst with strong critical thinking skills",
|
||||
self_reflect=True,
|
||||
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
|
||||
verbose=True,
|
||||
tools=[duckduckgo_search]
|
||||
)
|
||||
|
||||
# Create a task
|
||||
task = Task(
|
||||
description="Search for recent developments in AI and provide a brief analysis",
|
||||
expected_output="A detailed analysis report",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Create and start the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
try:
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
print(f"Result with tools: {result}")
|
||||
|
||||
assert result, "Self-reflection with tools failed to produce a result."
|
||||
print("\n✅ SUCCESS: Self-reflection with tools is working!")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {str(e)}")
|
||||
raise AssertionError(f"Test with tools failed: {str(e)}")
|
||||
|
||||
def test_self_reflection_without_tools():
|
||||
"""Test self-reflection without tools - should work (baseline)"""
|
||||
print("\n=== Testing Self-Reflection WITHOUT Tools ===")
|
||||
|
||||
# Create an agent with self-reflection but no tools
|
||||
agent = Agent(
|
||||
role="Senior Research Analyst",
|
||||
goal="Analyze and provide insights on given topics",
|
||||
backstory="You are an expert analyst with strong critical thinking skills",
|
||||
self_reflect=True,
|
||||
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Create a task
|
||||
task = Task(
|
||||
description="Analyze recent developments in AI",
|
||||
expected_output="A detailed analysis report",
|
||||
agent=agent
|
||||
)
|
||||
|
||||
# Create and start the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
try:
|
||||
# Start execution
|
||||
result = agents.start()
|
||||
print(f"Result without tools: {result}")
|
||||
|
||||
assert result, "Self-reflection without tools failed to produce a result."
|
||||
print("\n✅ SUCCESS: Self-reflection without tools is working!")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {str(e)}")
|
||||
raise AssertionError(f"Test without tools failed: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing self-reflection fix...")
|
||||
|
||||
# Test without tools (should work)
|
||||
try:
|
||||
result_without_tools = test_self_reflection_without_tools()
|
||||
without_tools_success = True
|
||||
except Exception as e:
|
||||
print(f"Test without tools failed: {e}")
|
||||
without_tools_success = False
|
||||
|
||||
# Test with tools (should work after fix)
|
||||
try:
|
||||
result_with_tools = test_self_reflection_with_tools()
|
||||
with_tools_success = True
|
||||
except Exception as e:
|
||||
print(f"Test with tools failed: {e}")
|
||||
with_tools_success = False
|
||||
|
||||
print("\n=== Test Summary ===")
|
||||
print(f"Without tools: {'SUCCESS' if without_tools_success else 'FAILED'}")
|
||||
print(f"With tools: {'SUCCESS' if with_tools_success else 'FAILED'}")
|
||||
|
||||
if with_tools_success:
|
||||
print("\n✅ Fix verified: Self-reflection now works with tools!")
|
||||
else:
|
||||
print("\n❌ Fix failed: Self-reflection still not working with tools")
|
||||
raise AssertionError("Self-reflection with tools test failed")
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Simple test to verify the self-reflection fix works"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
def test_self_reflection_fix():
|
||||
"""Test that self-reflection works with tools after the fix"""
|
||||
print("=== Testing Self-Reflection Fix ===")
|
||||
|
||||
# Create an agent with self-reflection and a simple tool
|
||||
agent = Agent(
|
||||
role="Math Assistant",
|
||||
goal="Solve math problems accurately",
|
||||
backstory="You are a helpful math assistant",
|
||||
self_reflect=True,
|
||||
llm="gpt-4o-mini", # Use a more widely available model
|
||||
verbose=True,
|
||||
tools=[calculator],
|
||||
min_reflect=1,
|
||||
max_reflect=2
|
||||
)
|
||||
|
||||
# Test with a simple calculation that might trigger self-reflection
|
||||
try:
|
||||
response = agent.start("What is 25 * 17? Show your work and double-check the answer.")
|
||||
print(f"\nResponse: {response}")
|
||||
|
||||
assert response, "Self-reflection with tools failed to produce a response."
|
||||
print("\n✅ SUCCESS: Self-reflection with tools is working!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {str(e)}")
|
||||
raise AssertionError(f"Test failed with error: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_self_reflection_fix()
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test to verify the self-reflection fix works with tools"""
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
def test_self_reflection_fix_verification():
|
||||
"""Test that self-reflection now works with tools"""
|
||||
print("=== Testing Self-Reflection Fix Verification ===")
|
||||
|
||||
# Create an agent with self-reflection enabled and tools
|
||||
agent = Agent(
|
||||
name="MathAgent",
|
||||
role="Math Assistant",
|
||||
goal="Solve mathematical problems accurately",
|
||||
backstory="You are a helpful math assistant who double-checks calculations",
|
||||
tools=[calculator],
|
||||
self_reflect=True, # This should now work with tools
|
||||
min_reflect=1,
|
||||
max_reflect=2,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Define a task that would benefit from self-reflection
|
||||
task = Task(
|
||||
description="Calculate 123 * 456 and verify the result is correct",
|
||||
expected_output="The calculation result with verification",
|
||||
agent=agent,
|
||||
name="math_calculation"
|
||||
)
|
||||
|
||||
# Create and run the agents
|
||||
agents = PraisonAIAgents(
|
||||
agents=[agent],
|
||||
tasks=[task],
|
||||
process="sequential"
|
||||
)
|
||||
|
||||
try:
|
||||
result = agents.start()
|
||||
|
||||
assert result, "Self-reflection with tools failed to produce a result."
|
||||
print("\n✅ SUCCESS: Self-reflection with tools is working!")
|
||||
print(f"Result: {result}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {str(e)}")
|
||||
raise AssertionError(f"Test failed with error: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_self_reflection_fix_verification()
|
||||
Generated
+1
-1
@@ -2375,7 +2375,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.130"
|
||||
version = "0.0.131"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.56.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.56.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.57.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.57.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -57,7 +57,7 @@ class CloudDeployer:
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.56 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.2.57 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.56"
|
||||
version = "2.2.57"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
readme = "README.md"
|
||||
license = ""
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.130",
|
||||
"praisonaiagents>=0.0.131",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
|
||||
|
||||
[tool.poetry]
|
||||
name = "PraisonAI"
|
||||
version = "2.2.56"
|
||||
version = "2.2.57"
|
||||
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
|
||||
authors = ["Mervin Praison"]
|
||||
license = ""
|
||||
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
|
||||
rich = ">=13.7"
|
||||
markdown = ">=3.5"
|
||||
pyparsing = ">=3.0.0"
|
||||
praisonaiagents = ">=0.0.130"
|
||||
praisonaiagents = ">=0.0.131"
|
||||
python-dotenv = ">=0.19.0"
|
||||
instructor = ">=1.3.3"
|
||||
PyYAML = ">=6.0"
|
||||
|
||||
Generated
+5
-5
@@ -3932,7 +3932,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonai"
|
||||
version = "2.2.56"
|
||||
version = "2.2.57"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "instructor" },
|
||||
@@ -4074,7 +4074,7 @@ requires-dist = [
|
||||
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.130" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.131" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4132,7 +4132,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.130"
|
||||
version = "0.0.131"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -4142,9 +4142,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/30/3a/e4f4bf6724051ada87c8f4a02615075b7c9b3963bdb37268d79f8a2bde08/praisonaiagents-0.0.130.tar.gz", hash = "sha256:0f893fdb65b68b489d7a9daf732a9c0c8eb2ce942d2c7b1c073d41e9496da239", size = 193079, upload-time = "2025-07-14T16:26:59.782Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ac/a8/6c8b10f2254a5914a0c282e8691bea7f3fff60ea99d975db11e8d3382c9f/praisonaiagents-0.0.131.tar.gz", hash = "sha256:8b6ceef477f2c1d4e33af1242c52cf1956826066ef4d7ed3bc2dcd50ce1c68c8", size = 198480, upload-time = "2025-07-14T17:35:51.53Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/8e/54f1858a001dbaf0a97b31406e06bfd47cbdf335b2ee509f591fcd4311de/praisonaiagents-0.0.130-py3-none-any.whl", hash = "sha256:c75983e55f9a0bd74905c28f7000acdd6d5516e519ce2aa5ef3e37048544ea6e", size = 214415, upload-time = "2025-07-14T16:26:58.109Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/d7/c443377ac2eaf68154b4ae6f87e5bc681f80774f8af9fcb368454ea9a7d8/praisonaiagents-0.0.131-py3-none-any.whl", hash = "sha256:fac2cdfe78da0fea89a74d54aab2fa9e920d61ca8d08ccebeb58132fc3770dc8", size = 218389, upload-time = "2025-07-14T17:35:49.94Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user