acra-cli 0.1.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.
- acra/__init__.py +7 -0
- acra/__main__.py +4 -0
- acra/agents/__init__.py +0 -0
- acra/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
- acra/agents/__pycache__/llm_utils.cpython-312.pyc +0 -0
- acra/agents/coder/__init__.py +0 -0
- acra/agents/coder/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/coder/__pycache__/coder_agent.cpython-312.pyc +0 -0
- acra/agents/coder/__pycache__/coder_chain.cpython-312.pyc +0 -0
- acra/agents/coder/coder_agent.py +235 -0
- acra/agents/coder/coder_chain.py +204 -0
- acra/agents/critic/__init__.py +0 -0
- acra/agents/critic/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/critic/__pycache__/critic_agent.cpython-312.pyc +0 -0
- acra/agents/critic/__pycache__/critic_chain.cpython-312.pyc +0 -0
- acra/agents/critic/critic_agent.py +199 -0
- acra/agents/critic/critic_chain.py +268 -0
- acra/agents/executor/__init__.py +0 -0
- acra/agents/executor/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/executor/__pycache__/docker_runner.cpython-312.pyc +0 -0
- acra/agents/executor/__pycache__/executor_agent.cpython-312.pyc +0 -0
- acra/agents/executor/__pycache__/sandbox_runner.cpython-312.pyc +0 -0
- acra/agents/executor/docker_runner.py +114 -0
- acra/agents/executor/executor_agent.py +142 -0
- acra/agents/executor/sandbox_runner.py +144 -0
- acra/agents/human/__init__.py +0 -0
- acra/agents/human/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/human/__pycache__/human_node.cpython-312.pyc +0 -0
- acra/agents/human/human_node.py +37 -0
- acra/agents/llm.py +263 -0
- acra/agents/llm_utils.py +161 -0
- acra/agents/memory/__init__.py +0 -0
- acra/agents/memory/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/memory/__pycache__/memory_agent.cpython-312.pyc +0 -0
- acra/agents/memory/__pycache__/memory_manager.cpython-312.pyc +0 -0
- acra/agents/memory/__pycache__/retrieval.cpython-312.pyc +0 -0
- acra/agents/memory/memory_agent.py +239 -0
- acra/agents/memory/memory_manager.py +273 -0
- acra/agents/memory/retrieval.py +355 -0
- acra/agents/planner/__init__.py +0 -0
- acra/agents/planner/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/planner/__pycache__/planner_agent.cpython-312.pyc +0 -0
- acra/agents/planner/__pycache__/planner_chain.cpython-312.pyc +0 -0
- acra/agents/planner/planner_agent.py +97 -0
- acra/agents/planner/planner_chain.py +160 -0
- acra/agents/researcher/__init__.py +1 -0
- acra/agents/researcher/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/agents/researcher/__pycache__/researcher_agent.cpython-312.pyc +0 -0
- acra/agents/researcher/__pycache__/researcher_chain.cpython-312.pyc +0 -0
- acra/agents/researcher/researcher_agent.py +231 -0
- acra/agents/researcher/researcher_chain.py +193 -0
- acra/brain/__init__.py +6 -0
- acra/brain/model_registry.py +19 -0
- acra/brain/provider_manager.py +21 -0
- acra/cli.py +52 -0
- acra/commands/__init__.py +29 -0
- acra/commands/ask.py +49 -0
- acra/commands/brain.py +58 -0
- acra/commands/config.py +25 -0
- acra/commands/context.py +30 -0
- acra/commands/graph.py +18 -0
- acra/commands/keys.py +40 -0
- acra/commands/logs.py +13 -0
- acra/commands/memory.py +27 -0
- acra/commands/plugin.py +18 -0
- acra/commands/research.py +65 -0
- acra/commands/serve.py +13 -0
- acra/commands/session.py +20 -0
- acra/commands/utils.py +12 -0
- acra/config/__init__.py +36 -0
- acra/config/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/config/__pycache__/defaults.cpython-312.pyc +0 -0
- acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
- acra/config/defaults.py +88 -0
- acra/config/profile_manager.py +87 -0
- acra/execution/__init__.py +1 -0
- acra/execution/logs/__init__.py +1 -0
- acra/execution/logs/error_logs/__init__.py +1 -0
- acra/execution/logs/execution_logs/__init__.py +1 -0
- acra/execution/sandbox/__init__.py +1 -0
- acra/execution/sandbox/docker_executor.py +209 -0
- acra/execution/sandbox/isolated_runner.py +148 -0
- acra/execution/sandbox/sandbox_manager.py +178 -0
- acra/graph/__init__.py +0 -0
- acra/graph/checkpoint.py +85 -0
- acra/graph/conditional_edges.py +103 -0
- acra/graph/edges.py +85 -0
- acra/graph/graph_visualizer.py +175 -0
- acra/graph/nodes.py +68 -0
- acra/graph/router.py +69 -0
- acra/graph/state.py +93 -0
- acra/graph/workflow.py +117 -0
- acra/memory/__init__.py +1 -0
- acra/memory/checkpoints/__init__.py +1 -0
- acra/memory/checkpoints/data/__init__.py +1 -0
- acra/memory/checkpoints/postgres_checkpoint.py +247 -0
- acra/memory/checkpoints/sqlite_checkpoint.py +309 -0
- acra/memory/chroma_db/__init__.py +1 -0
- acra/memory/conversation_memory/__init__.py +1 -0
- acra/memory/conversation_memory/long_term.py +229 -0
- acra/memory/conversation_memory/short_term.py +158 -0
- acra/memory/storage/__init__.py +1 -0
- acra/memory/vector_store/__init__.py +0 -0
- acra/memory/vector_store/chroma_store.py +260 -0
- acra/memory/vector_store/embeddings.py +43 -0
- acra/observability/__init__.py +1 -0
- acra/observability/langsmith_tracing.py +91 -0
- acra/observability/metrics.py +189 -0
- acra/observability/monitoring.py +162 -0
- acra/observability/token_tracking.py +131 -0
- acra/prompts/__init__.py +1 -0
- acra/schemas/__init__.py +0 -0
- acra/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/coder_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/critic_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/execution_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/planner_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/researcher_schema.cpython-312.pyc +0 -0
- acra/schemas/__pycache__/shared_schema.cpython-312.pyc +0 -0
- acra/schemas/coder_schema.py +257 -0
- acra/schemas/critic_schema.py +177 -0
- acra/schemas/execution_schema.py +74 -0
- acra/schemas/planner_schema.py +129 -0
- acra/schemas/researcher_schema.py +220 -0
- acra/schemas/shared_schema.py +329 -0
- acra/tools/__init__.py +1 -0
- acra/tools/code_tools/__init__.py +1 -0
- acra/tools/code_tools/file_reader.py +156 -0
- acra/tools/code_tools/file_writer.py +108 -0
- acra/tools/code_tools/python_repl.py +72 -0
- acra/tools/code_tools/terminal_runner.py +98 -0
- acra/tools/github_tools/__init__.py +1 -0
- acra/tools/github_tools/commit_generator.py +91 -0
- acra/tools/github_tools/github_search.py +113 -0
- acra/tools/github_tools/repo_analyzer.py +138 -0
- acra/tools/integration_test_helper.py +370 -0
- acra/tools/validation_tools/__init__.py +1 -0
- acra/tools/validation_tools/dependency_checker.py +101 -0
- acra/tools/validation_tools/environment_config_validator.py +353 -0
- acra/tools/validation_tools/http_validator.py +336 -0
- acra/tools/validation_tools/response_validator.py +407 -0
- acra/tools/validation_tools/security_checker.py +93 -0
- acra/tools/validation_tools/syntax_checker.py +75 -0
- acra/tools/validation_tools/web_framework_validator.py +382 -0
- acra/tools/web_tools/__init__.py +1 -0
- acra/tools/web_tools/arxiv_search.py +110 -0
- acra/tools/web_tools/serpapi_search.py +106 -0
- acra/tools/web_tools/tavily_search.py +96 -0
- acra/ui/__init__.py +8 -0
- acra/ui/__pycache__/__init__.cpython-312.pyc +0 -0
- acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
- acra/ui/__pycache__/components.cpython-312.pyc +0 -0
- acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
- acra/ui/__pycache__/theme.cpython-312.pyc +0 -0
- acra/ui/banner.py +40 -0
- acra/ui/components.py +32 -0
- acra/ui/shell.py +91 -0
- acra/ui/theme.py +28 -0
- acra/utils/__init__.py +21 -0
- acra/utils/context_manager.py +20 -0
- acra/utils/keyring_manager.py +83 -0
- acra/utils/output_formatter.py +18 -0
- acra/utils/session_manager.py +59 -0
- acra_cli-0.1.1.dist-info/METADATA +616 -0
- acra_cli-0.1.1.dist-info/RECORD +169 -0
- acra_cli-0.1.1.dist-info/WHEEL +5 -0
- acra_cli-0.1.1.dist-info/licenses/LICENSE +661 -0
- acra_cli-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Human approval node.
|
|
3
|
+
Pauses the workflow and waits for human input via stdin (CLI) or sets
|
|
4
|
+
approved=False automatically in non-interactive environments.
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Dict
|
|
8
|
+
from langchain_core.messages import AIMessage
|
|
9
|
+
from acra.graph.state import AgentState
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def human_node(state: AgentState) -> Dict:
|
|
13
|
+
"""
|
|
14
|
+
Human-in-the-loop approval node.
|
|
15
|
+
In a TTY environment, prompts the user. Otherwise auto-denies.
|
|
16
|
+
"""
|
|
17
|
+
reason = state.get("critic_feedback", "Agent requested human review.")
|
|
18
|
+
print(f"\n[HUMAN APPROVAL REQUIRED]\n{reason}\n")
|
|
19
|
+
|
|
20
|
+
approved = False
|
|
21
|
+
feedback = ""
|
|
22
|
+
|
|
23
|
+
if sys.stdin.isatty():
|
|
24
|
+
answer = input("Approve? (y/n): ").strip().lower()
|
|
25
|
+
approved = answer == "y"
|
|
26
|
+
if not approved:
|
|
27
|
+
feedback = input("Feedback (optional): ").strip()
|
|
28
|
+
else:
|
|
29
|
+
print("Non-interactive environment — auto-denying.")
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
"messages": [AIMessage(content=f"Human node: {'Approved' if approved else 'Denied'}.")],
|
|
33
|
+
"approved": approved,
|
|
34
|
+
"human_feedback": feedback,
|
|
35
|
+
"next_agent": "coder" if approved else "end",
|
|
36
|
+
"current_agent": "human",
|
|
37
|
+
}
|
acra/agents/llm.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM initialisation for OmniAgent.
|
|
3
|
+
Supports multiple LLM providers: Google Gemini, OpenAI, Groq, Ollama, HuggingFace.
|
|
4
|
+
Cached as a singleton — calling llm() multiple times returns the same instance.
|
|
5
|
+
"""
|
|
6
|
+
import functools
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Union
|
|
10
|
+
|
|
11
|
+
from dotenv import load_dotenv
|
|
12
|
+
|
|
13
|
+
load_dotenv()
|
|
14
|
+
|
|
15
|
+
# Import provider-specific modules
|
|
16
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from langchain_openai import ChatOpenAI
|
|
20
|
+
OPENAI_AVAILABLE = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
ChatOpenAI = None
|
|
23
|
+
OPENAI_AVAILABLE = False
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
from langchain_groq import ChatGroq
|
|
27
|
+
GROQ_AVAILABLE = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
ChatGroq = None
|
|
30
|
+
GROQ_AVAILABLE = False
|
|
31
|
+
|
|
32
|
+
# HuggingFace imports (handled gracefully if not installed)
|
|
33
|
+
try:
|
|
34
|
+
from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace
|
|
35
|
+
from langchain_huggingface.llms import HuggingFacePipeline
|
|
36
|
+
HF_AVAILABLE = True
|
|
37
|
+
except ImportError:
|
|
38
|
+
HuggingFaceEndpoint = None
|
|
39
|
+
ChatHuggingFace = None
|
|
40
|
+
HuggingFacePipeline = None
|
|
41
|
+
HF_AVAILABLE = False
|
|
42
|
+
|
|
43
|
+
# Ollama imports (handled gracefully if not installed)
|
|
44
|
+
try:
|
|
45
|
+
from langchain_ollama import OllamaLLM
|
|
46
|
+
OLLAMA_AVAILABLE = True
|
|
47
|
+
except ImportError:
|
|
48
|
+
OllamaLLM = None
|
|
49
|
+
OLLAMA_AVAILABLE = False
|
|
50
|
+
|
|
51
|
+
if TYPE_CHECKING:
|
|
52
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
53
|
+
from langchain_openai import ChatOpenAI
|
|
54
|
+
from langchain_groq import ChatGroq
|
|
55
|
+
from langchain_ollama import OllamaLLM
|
|
56
|
+
from langchain_huggingface import ChatHuggingFace
|
|
57
|
+
from langchain_huggingface.llms import HuggingFacePipeline
|
|
58
|
+
|
|
59
|
+
from acra.config import (
|
|
60
|
+
LLM_PROVIDER, LLM_TEMPERATURE,
|
|
61
|
+
GEMINI_MODEL,
|
|
62
|
+
OPENAI_MODEL,
|
|
63
|
+
GROQ_MODEL,
|
|
64
|
+
OLLAMA_MODEL, OLLAMA_BASE_URL,
|
|
65
|
+
HF_MODEL, HF_DEVICE, HF_LOCAL_REPO,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class LLMInitializationError(Exception):
|
|
70
|
+
"""Raised when LLM initialization fails."""
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _init_gemini():
|
|
75
|
+
"""Initialize Google Gemini LLM."""
|
|
76
|
+
gemini_api_key = os.getenv("GOOGLE_GEMINI_API_KEY") or os.getenv("GEMINI_API_KEY")
|
|
77
|
+
if not gemini_api_key or gemini_api_key.startswith("your_"):
|
|
78
|
+
raise EnvironmentError(
|
|
79
|
+
"GEMINI_API_KEY is not set. Add GOOGLE_GEMINI_API_KEY to your .env file."
|
|
80
|
+
)
|
|
81
|
+
os.environ["GOOGLE_API_KEY"] = gemini_api_key
|
|
82
|
+
return ChatGoogleGenerativeAI(
|
|
83
|
+
model=GEMINI_MODEL,
|
|
84
|
+
temperature=LLM_TEMPERATURE
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _init_openai():
|
|
89
|
+
"""Initialize OpenAI LLM."""
|
|
90
|
+
if not OPENAI_AVAILABLE:
|
|
91
|
+
raise LLMInitializationError(
|
|
92
|
+
"OpenAI support is not installed. Install with: pip install langchain-openai"
|
|
93
|
+
)
|
|
94
|
+
openai_api_key = os.getenv("OPENAI_API_KEY")
|
|
95
|
+
if not openai_api_key or openai_api_key.startswith("your_"):
|
|
96
|
+
raise LLMInitializationError(
|
|
97
|
+
"OPENAI_API_KEY is not set. Add OPENAI_API_KEY to your .env file."
|
|
98
|
+
)
|
|
99
|
+
return ChatOpenAI(
|
|
100
|
+
model=OPENAI_MODEL,
|
|
101
|
+
api_key=openai_api_key,
|
|
102
|
+
temperature=LLM_TEMPERATURE
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _init_groq():
|
|
107
|
+
"""Initialize Groq LLM."""
|
|
108
|
+
if not GROQ_AVAILABLE:
|
|
109
|
+
raise LLMInitializationError(
|
|
110
|
+
"Groq support is not installed. Install with: pip install langchain-groq"
|
|
111
|
+
)
|
|
112
|
+
groq_api_key = os.getenv("GROQ_API_KEY")
|
|
113
|
+
if not groq_api_key or groq_api_key.startswith("your_"):
|
|
114
|
+
raise LLMInitializationError(
|
|
115
|
+
"GROQ_API_KEY is not set. Add GROQ_API_KEY to your .env file."
|
|
116
|
+
)
|
|
117
|
+
return ChatGroq(
|
|
118
|
+
model=GROQ_MODEL,
|
|
119
|
+
api_key=groq_api_key,
|
|
120
|
+
temperature=LLM_TEMPERATURE
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _init_ollama():
|
|
125
|
+
"""Initialize Ollama LLM (local inference)."""
|
|
126
|
+
if not OLLAMA_AVAILABLE:
|
|
127
|
+
raise LLMInitializationError(
|
|
128
|
+
"Ollama not installed. Install with: pip install langchain-ollama"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
try:
|
|
132
|
+
# Test connection
|
|
133
|
+
llm = OllamaLLM(
|
|
134
|
+
model=OLLAMA_MODEL,
|
|
135
|
+
base_url=OLLAMA_BASE_URL,
|
|
136
|
+
temperature=LLM_TEMPERATURE
|
|
137
|
+
)
|
|
138
|
+
return llm
|
|
139
|
+
except Exception as e:
|
|
140
|
+
raise LLMInitializationError(
|
|
141
|
+
f"Failed to connect to Ollama at {OLLAMA_BASE_URL}. "
|
|
142
|
+
f"Ensure Ollama is running with: ollama serve\nError: {e}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _init_huggingface_cloud():
|
|
147
|
+
"""Initialize HuggingFace cloud-based LLM (via API)."""
|
|
148
|
+
if not HF_AVAILABLE:
|
|
149
|
+
raise LLMInitializationError(
|
|
150
|
+
"HuggingFace not installed. Install with: pip install langchain-huggingface"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
huggingface_api_key = os.getenv("HF_API_KEY")
|
|
154
|
+
if not huggingface_api_key or huggingface_api_key.startswith("your_"):
|
|
155
|
+
raise LLMInitializationError(
|
|
156
|
+
"HF_API_KEY is not set. Add HF_API_KEY to your .env file. "
|
|
157
|
+
"Get a token at: https://huggingface.co/settings/tokens"
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
endpoint = HuggingFaceEndpoint(
|
|
161
|
+
repo_id=HF_MODEL,
|
|
162
|
+
huggingfacehub_api_token=huggingface_api_key,
|
|
163
|
+
temperature=LLM_TEMPERATURE
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return ChatHuggingFace(llm=endpoint)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _init_huggingface_local():
|
|
170
|
+
"""Initialize HuggingFace local model (requires transformers & torch)."""
|
|
171
|
+
if not HF_AVAILABLE:
|
|
172
|
+
raise LLMInitializationError(
|
|
173
|
+
"HuggingFace not installed. Install with: pip install langchain-huggingface"
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
from transformers import pipeline
|
|
178
|
+
|
|
179
|
+
# Create local repo directory
|
|
180
|
+
repo_path = Path(HF_LOCAL_REPO)
|
|
181
|
+
repo_path.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
|
|
183
|
+
# Initialize pipeline
|
|
184
|
+
pipe = pipeline(
|
|
185
|
+
"text-generation",
|
|
186
|
+
model=HF_MODEL,
|
|
187
|
+
device=0 if HF_DEVICE == "cuda" else -1, # 0 for GPU, -1 for CPU
|
|
188
|
+
model_kwargs={"cache_dir": str(repo_path)}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
llm = HuggingFacePipeline(
|
|
192
|
+
model_id=HF_MODEL,
|
|
193
|
+
task="text-generation",
|
|
194
|
+
pipeline=pipe
|
|
195
|
+
)
|
|
196
|
+
return llm
|
|
197
|
+
except ImportError as e:
|
|
198
|
+
raise LLMInitializationError(
|
|
199
|
+
f"HuggingFace local model requires transformers and torch. "
|
|
200
|
+
f"Install with: pip install transformers torch\nError: {e}"
|
|
201
|
+
)
|
|
202
|
+
except Exception as e:
|
|
203
|
+
raise LLMInitializationError(
|
|
204
|
+
f"Failed to initialize HuggingFace local model {HF_MODEL}. "
|
|
205
|
+
f"Error: {e}"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
@functools.lru_cache(maxsize=32)
|
|
210
|
+
def _get_cached_llm(
|
|
211
|
+
provider: str,
|
|
212
|
+
gemini_api_key: str,
|
|
213
|
+
openai_api_key: str,
|
|
214
|
+
groq_api_key: str,
|
|
215
|
+
hf_api_key: str,
|
|
216
|
+
ollama_model: str,
|
|
217
|
+
ollama_base_url: str,
|
|
218
|
+
hf_model: str,
|
|
219
|
+
hf_device: str,
|
|
220
|
+
hf_local_repo: str,
|
|
221
|
+
temperature: str,
|
|
222
|
+
):
|
|
223
|
+
if provider == "gemini":
|
|
224
|
+
return _init_gemini()
|
|
225
|
+
if provider == "openai":
|
|
226
|
+
return _init_openai()
|
|
227
|
+
if provider == "groq":
|
|
228
|
+
return _init_groq()
|
|
229
|
+
if provider == "ollama":
|
|
230
|
+
return _init_ollama()
|
|
231
|
+
if provider == "huggingface_local":
|
|
232
|
+
return _init_huggingface_local()
|
|
233
|
+
if provider == "huggingface_cloud":
|
|
234
|
+
return _init_huggingface_cloud()
|
|
235
|
+
raise LLMInitializationError(
|
|
236
|
+
f"Unknown LLM_PROVIDER: {provider}. "
|
|
237
|
+
f"Supported providers: gemini, openai, groq, ollama, huggingface_local, huggingface_cloud"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def llm() -> Union[
|
|
242
|
+
"ChatGoogleGenerativeAI",
|
|
243
|
+
"ChatOpenAI",
|
|
244
|
+
"ChatGroq",
|
|
245
|
+
"OllamaLLM",
|
|
246
|
+
"ChatHuggingFace",
|
|
247
|
+
"HuggingFacePipeline",
|
|
248
|
+
]:
|
|
249
|
+
"""Return a cached LLM instance based on the current provider configuration."""
|
|
250
|
+
provider = os.getenv("LLM_PROVIDER", LLM_PROVIDER).lower().strip()
|
|
251
|
+
return _get_cached_llm(
|
|
252
|
+
provider,
|
|
253
|
+
os.getenv("GOOGLE_GEMINI_API_KEY") or os.getenv("GEMINI_API_KEY"),
|
|
254
|
+
os.getenv("OPENAI_API_KEY"),
|
|
255
|
+
os.getenv("GROQ_API_KEY"),
|
|
256
|
+
os.getenv("HF_API_KEY"),
|
|
257
|
+
os.getenv("OLLAMA_MODEL", OLLAMA_MODEL),
|
|
258
|
+
os.getenv("OLLAMA_BASE_URL", OLLAMA_BASE_URL),
|
|
259
|
+
os.getenv("HF_MODEL", HF_MODEL),
|
|
260
|
+
os.getenv("HF_DEVICE", HF_DEVICE),
|
|
261
|
+
os.getenv("HF_LOCAL_REPO", HF_LOCAL_REPO),
|
|
262
|
+
str(LLM_TEMPERATURE),
|
|
263
|
+
)
|
acra/agents/llm_utils.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LLM provider utilities for testing and debugging.
|
|
3
|
+
Run with: python -m agents.llm_utils
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Add project root to path
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
10
|
+
|
|
11
|
+
from acra.agents.llm import llm, LLMInitializationError
|
|
12
|
+
from acra.config import (
|
|
13
|
+
LLM_PROVIDER, LLM_TEMPERATURE,
|
|
14
|
+
GEMINI_API_KEY, OPENAI_API_KEY, GROQ_API_KEY,
|
|
15
|
+
HF_API_KEY, OLLAMA_BASE_URL, HF_MODEL, OLLAMA_MODEL
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def print_config():
|
|
20
|
+
"""Print current LLM configuration."""
|
|
21
|
+
print("\n" + "="*60)
|
|
22
|
+
print("OmniAgent LLM Configuration")
|
|
23
|
+
print("="*60)
|
|
24
|
+
print(f"\nProvider: {LLM_PROVIDER}")
|
|
25
|
+
print(f"Temperature: {LLM_TEMPERATURE}")
|
|
26
|
+
|
|
27
|
+
print("\n" + "-"*60)
|
|
28
|
+
print("Credentials Status:")
|
|
29
|
+
print("-"*60)
|
|
30
|
+
print(f"Gemini API Key: {'✓ Set' if GEMINI_API_KEY else '✗ Not set'}")
|
|
31
|
+
print(f"OpenAI API Key: {'✓ Set' if OPENAI_API_KEY else '✗ Not set'}")
|
|
32
|
+
print(f"Groq API Key: {'✓ Set' if GROQ_API_KEY else '✗ Not set'}")
|
|
33
|
+
print(f"HuggingFace Token: {'✓ Set' if HF_API_KEY else '✗ Not set'}")
|
|
34
|
+
|
|
35
|
+
print("\n" + "-"*60)
|
|
36
|
+
print("Provider-Specific Config:")
|
|
37
|
+
print("-"*60)
|
|
38
|
+
print(f"Ollama Base URL: {OLLAMA_BASE_URL}")
|
|
39
|
+
print(f"Ollama Model: {OLLAMA_MODEL}")
|
|
40
|
+
print(f"HuggingFace Model: {HF_MODEL}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_provider():
|
|
44
|
+
"""Test if the current LLM provider works."""
|
|
45
|
+
print("\n" + "="*60)
|
|
46
|
+
print(f"Testing LLM Provider: {LLM_PROVIDER}")
|
|
47
|
+
print("="*60 + "\n")
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
print(f"Initializing {LLM_PROVIDER} provider...", end=" ")
|
|
51
|
+
llm_instance = llm()
|
|
52
|
+
print("✓ Success!\n")
|
|
53
|
+
|
|
54
|
+
print(f"Provider type: {type(llm_instance).__name__}")
|
|
55
|
+
print(f"Model: {getattr(llm_instance, 'model_name', getattr(llm_instance, 'model', 'Unknown'))}")
|
|
56
|
+
|
|
57
|
+
# Test a simple prompt
|
|
58
|
+
print("\nTesting with simple prompt: 'Say hello'")
|
|
59
|
+
print("-"*60)
|
|
60
|
+
response = llm_instance.invoke("Say hello in one sentence.")
|
|
61
|
+
print(response.content if hasattr(response, 'content') else str(response))
|
|
62
|
+
print("-"*60)
|
|
63
|
+
print("\n✓ Provider test successful!")
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
except LLMInitializationError as e:
|
|
67
|
+
print(f"\n✗ Initialization Error:\n{e}")
|
|
68
|
+
return False
|
|
69
|
+
except Exception as e:
|
|
70
|
+
print(f"\n✗ Error: {e}")
|
|
71
|
+
print("\nTroubleshooting tips:")
|
|
72
|
+
if "connection" in str(e).lower():
|
|
73
|
+
print("- Check your internet connection")
|
|
74
|
+
print("- Verify the API endpoint is accessible")
|
|
75
|
+
if "key" in str(e).lower() or "auth" in str(e).lower():
|
|
76
|
+
print("- Verify your API credentials in .env file")
|
|
77
|
+
print("\nFor more help, see docs/LLM_PROVIDER_GUIDE.md")
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def list_providers():
|
|
82
|
+
"""List all available providers and their status."""
|
|
83
|
+
print("\n" + "="*60)
|
|
84
|
+
print("Available LLM Providers")
|
|
85
|
+
print("="*60 + "\n")
|
|
86
|
+
|
|
87
|
+
providers = {
|
|
88
|
+
"gemini": {
|
|
89
|
+
"description": "Google Gemini API",
|
|
90
|
+
"credential_env": "GOOGLE_GEMINI_API_KEY",
|
|
91
|
+
"setup": "https://aistudio.google.com/app/apikeys"
|
|
92
|
+
},
|
|
93
|
+
"openai": {
|
|
94
|
+
"description": "OpenAI GPT models",
|
|
95
|
+
"credential_env": "OPENAI_API_KEY",
|
|
96
|
+
"setup": "https://platform.openai.com/api-keys"
|
|
97
|
+
},
|
|
98
|
+
"groq": {
|
|
99
|
+
"description": "Groq API (fastest inference)",
|
|
100
|
+
"credential_env": "GROQ_API_KEY",
|
|
101
|
+
"setup": "https://console.groq.com/keys"
|
|
102
|
+
},
|
|
103
|
+
"ollama": {
|
|
104
|
+
"description": "Local Ollama inference",
|
|
105
|
+
"credential_env": "None (local)",
|
|
106
|
+
"setup": "https://ollama.ai"
|
|
107
|
+
},
|
|
108
|
+
"huggingface_local": {
|
|
109
|
+
"description": "Local HuggingFace models",
|
|
110
|
+
"credential_env": "None (local)",
|
|
111
|
+
"setup": "pip install transformers torch"
|
|
112
|
+
},
|
|
113
|
+
"huggingface_cloud": {
|
|
114
|
+
"description": "HuggingFace Inference API",
|
|
115
|
+
"credential_env": "HF_API_KEY",
|
|
116
|
+
"setup": "https://huggingface.co/settings/tokens"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for name, info in providers.items():
|
|
121
|
+
is_current = "← CURRENT" if name == LLM_PROVIDER.lower() else ""
|
|
122
|
+
print(f"{name.upper()} {is_current}")
|
|
123
|
+
print(f" Description: {info['description']}")
|
|
124
|
+
print(f" Credential: {info['credential_env']}")
|
|
125
|
+
print(f" Setup: {info['setup']}")
|
|
126
|
+
print()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main():
|
|
130
|
+
"""Run diagnostic utilities."""
|
|
131
|
+
import argparse
|
|
132
|
+
|
|
133
|
+
parser = argparse.ArgumentParser(
|
|
134
|
+
description="OmniAgent LLM Provider Utilities"
|
|
135
|
+
)
|
|
136
|
+
parser.add_argument(
|
|
137
|
+
"command",
|
|
138
|
+
choices=["config", "test", "list"],
|
|
139
|
+
help="Command to run"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
args = parser.parse_args()
|
|
143
|
+
|
|
144
|
+
if args.command == "config":
|
|
145
|
+
print_config()
|
|
146
|
+
elif args.command == "test":
|
|
147
|
+
success = test_provider()
|
|
148
|
+
sys.exit(0 if success else 1)
|
|
149
|
+
elif args.command == "list":
|
|
150
|
+
list_providers()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if __name__ == "__main__":
|
|
154
|
+
if len(sys.argv) < 2:
|
|
155
|
+
print(__doc__)
|
|
156
|
+
print("\nUsage:")
|
|
157
|
+
print(" python -m agents.llm_utils config # Show current configuration")
|
|
158
|
+
print(" python -m agents.llm_utils test # Test the current provider")
|
|
159
|
+
print(" python -m agents.llm_utils list # List all available providers")
|
|
160
|
+
else:
|
|
161
|
+
main()
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from langchain_core.messages import AIMessage
|
|
4
|
+
|
|
5
|
+
from acra.graph.state import AgentState
|
|
6
|
+
|
|
7
|
+
from acra.agents.memory.memory_manager import (
|
|
8
|
+
get_memory_manager
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from acra.agents.memory.retrieval import (
|
|
12
|
+
get_memory_retrieval
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def memory_agent(state: AgentState) -> Dict:
|
|
17
|
+
"""
|
|
18
|
+
Memory Agent
|
|
19
|
+
|
|
20
|
+
Responsibilities:
|
|
21
|
+
- Persist workflow memories
|
|
22
|
+
- Store successful execution patterns
|
|
23
|
+
- Store runtime failures
|
|
24
|
+
- Build historical context
|
|
25
|
+
- Retrieve relevant memories
|
|
26
|
+
- Support long-term autonomous reasoning
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# extract state
|
|
30
|
+
|
|
31
|
+
session_id = state.get(
|
|
32
|
+
"session_id",
|
|
33
|
+
"default_session"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
user_request = state.get(
|
|
37
|
+
"user_request",
|
|
38
|
+
""
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
current_step = state.get(
|
|
42
|
+
"current_step",
|
|
43
|
+
""
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
workflow_status = state.get(
|
|
47
|
+
"workflow_status",
|
|
48
|
+
""
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
execution_success = state.get(
|
|
52
|
+
"execution_success",
|
|
53
|
+
False
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
execution_logs = state.get(
|
|
57
|
+
"execution_logs",
|
|
58
|
+
""
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
error_message = state.get(
|
|
62
|
+
"error_message",
|
|
63
|
+
""
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
generated_files = state.get(
|
|
67
|
+
"generated_files",
|
|
68
|
+
{}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
critic_feedback = state.get(
|
|
72
|
+
"critic_feedback",
|
|
73
|
+
""
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
quality_score = state.get(
|
|
77
|
+
"quality_score",
|
|
78
|
+
0
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
retry_count = state.get(
|
|
82
|
+
"retry_count",
|
|
83
|
+
0
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
next_agent = state.get(
|
|
87
|
+
"next_agent",
|
|
88
|
+
"end"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# initialize memory manager and retrieval system
|
|
93
|
+
|
|
94
|
+
memory_manager = get_memory_manager(
|
|
95
|
+
session_id=session_id
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
retrieval_system = get_memory_retrieval(
|
|
99
|
+
session_id=session_id
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# determine memory type
|
|
104
|
+
|
|
105
|
+
if execution_success and quality_score >= 8:
|
|
106
|
+
|
|
107
|
+
memory_type = "successful_execution"
|
|
108
|
+
|
|
109
|
+
elif error_message:
|
|
110
|
+
|
|
111
|
+
memory_type = "execution_failure"
|
|
112
|
+
|
|
113
|
+
elif critic_feedback:
|
|
114
|
+
|
|
115
|
+
memory_type = "critic_feedback"
|
|
116
|
+
|
|
117
|
+
else:
|
|
118
|
+
|
|
119
|
+
memory_type = "workflow_context"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# build memory content
|
|
123
|
+
|
|
124
|
+
memory_content = {
|
|
125
|
+
|
|
126
|
+
"user_request": user_request,
|
|
127
|
+
|
|
128
|
+
"current_step": current_step,
|
|
129
|
+
|
|
130
|
+
"workflow_status": workflow_status,
|
|
131
|
+
|
|
132
|
+
"execution_success": execution_success,
|
|
133
|
+
|
|
134
|
+
"quality_score": quality_score,
|
|
135
|
+
|
|
136
|
+
"retry_count": retry_count,
|
|
137
|
+
|
|
138
|
+
"error_message": error_message,
|
|
139
|
+
|
|
140
|
+
"critic_feedback": critic_feedback,
|
|
141
|
+
|
|
142
|
+
"generated_files": list(
|
|
143
|
+
generated_files.keys()
|
|
144
|
+
),
|
|
145
|
+
|
|
146
|
+
"execution_logs": (
|
|
147
|
+
execution_logs[:2000]
|
|
148
|
+
if execution_logs
|
|
149
|
+
else ""
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# store memory
|
|
155
|
+
|
|
156
|
+
memory_manager.add_memory(
|
|
157
|
+
memory_type=memory_type,
|
|
158
|
+
content=memory_content
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# retrieve relevant memories for context building
|
|
163
|
+
|
|
164
|
+
relevant_memories = (
|
|
165
|
+
retrieval_system.get_relevant_memories(
|
|
166
|
+
query=user_request,
|
|
167
|
+
limit=3
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# retrieve similar past failures if current execution failed
|
|
173
|
+
|
|
174
|
+
similar_failures = []
|
|
175
|
+
|
|
176
|
+
if error_message:
|
|
177
|
+
|
|
178
|
+
similar_failures = (
|
|
179
|
+
retrieval_system.get_error_solutions(
|
|
180
|
+
error_message=error_message,
|
|
181
|
+
limit=3
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# memory statistics
|
|
187
|
+
|
|
188
|
+
memory_stats = (
|
|
189
|
+
retrieval_system.get_memory_insights()
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# build memory message
|
|
194
|
+
|
|
195
|
+
if execution_success:
|
|
196
|
+
|
|
197
|
+
memory_message = (
|
|
198
|
+
"🧠 Memory Agent: "
|
|
199
|
+
"Stored successful execution pattern "
|
|
200
|
+
"for future optimization."
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
elif error_message:
|
|
204
|
+
|
|
205
|
+
memory_message = (
|
|
206
|
+
"🛠️ Memory Agent: "
|
|
207
|
+
"Stored runtime failure and "
|
|
208
|
+
"retrieved similar debugging history."
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
else:
|
|
212
|
+
|
|
213
|
+
memory_message = (
|
|
214
|
+
"📚 Memory Agent: "
|
|
215
|
+
"Workflow context stored successfully."
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# return updated state
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
|
|
223
|
+
"messages": [
|
|
224
|
+
AIMessage(content=memory_message)
|
|
225
|
+
],
|
|
226
|
+
|
|
227
|
+
# Memory Storage
|
|
228
|
+
"memory_type": memory_type,
|
|
229
|
+
|
|
230
|
+
"memory_stats": memory_stats,
|
|
231
|
+
|
|
232
|
+
# Retrieved Context
|
|
233
|
+
"memory_context": relevant_memories,
|
|
234
|
+
|
|
235
|
+
"similar_failures": similar_failures,
|
|
236
|
+
|
|
237
|
+
# Workflow
|
|
238
|
+
"next_agent": next_agent,
|
|
239
|
+
}
|