Skip to content

Commit 6a32fb2

Browse files
committed
reverted commit..
1 parent 4db1f38 commit 6a32fb2

5 files changed

Lines changed: 90 additions & 184 deletions

File tree

config.yaml

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,13 @@
11
# AI Agent Model Configuration
22

3-
# Default/fallback model (used for CLI and initial startup)
4-
agent_model:
5-
name: "gpt-5.1"
6-
base_url: null # null for default OpenAI endpoint
7-
api_key_env: "OPENAI_API_KEY" # Environment variable containing API key
8-
9-
# Default model for UI dropdown (display_name from available_models)
10-
default_ui_model: "gpt-5.1"
3+
# Default config
4+
# agent_model:
5+
# name: "gpt-5.1" # "gpt-4o" # Model name
6+
# base_url: null # null for default OpenAI endpoint
7+
# api_key_env: "OPENAI_API_KEY" # Environment variable containing API key
118

12-
# Available models for UI dropdown
13-
available_models:
14-
- display_name: "gpt-4o-mini"
15-
name: "gpt-4o-mini"
16-
base_url: null
17-
provider: "OpenAI"
18-
api_key_env: "OPENAI_API_KEY"
19-
20-
- display_name: "gpt-4o"
21-
name: "gpt-4o"
22-
base_url: null
23-
provider: "OpenAI"
24-
api_key_env: "OPENAI_API_KEY"
25-
26-
- display_name: "gpt-5-mini"
27-
name: "gpt-5-mini"
28-
base_url: null
29-
provider: "OpenAI"
30-
api_key_env: "OPENAI_API_KEY"
31-
32-
- display_name: "gpt-5.1"
33-
name: "gpt-5.1"
34-
base_url: null
35-
provider: "OpenAI"
36-
api_key_env: "OPENAI_API_KEY"
37-
38-
- display_name: "GPT-OSS-120B [EPFL]"
39-
name: "openai/gpt-oss-120b"
40-
base_url: "https://inference-rcp.epfl.ch/v1"
41-
provider: "EPFL"
42-
api_key_env: "EPFL_API_KEY"
9+
# Using EPFL's inference server
10+
agent_model:
11+
name: "openai/gpt-oss-120b"
12+
base_url: "https://inference.rcp.epfl.ch/v1"
13+
api_key_env: "EPFL_API_KEY" # Set EPFL_API_KEY in .env

src/ai_agent/agent/agent.py

Lines changed: 49 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66

77
from pydantic_ai import Agent, RunContext
88
from pydantic_ai.usage import UsageLimits
9-
from pydantic_ai.models.openai import OpenAIResponsesModel, OpenAIChatModel
9+
from pydantic_ai.models.openai import OpenAIResponsesModel
1010
from pydantic_ai.providers.openai import OpenAIProvider
1111
from pydantic_ai.messages import BinaryContent
1212

1313
from ai_agent.generator.prompts import get_agent_system_prompt
14-
from ai_agent.generator.schema import ToolSelection, Conversation, ConversationStatus
14+
from ai_agent.generator.schema import ToolSelection
1515
from ai_agent.utils.config import get_config
1616
from .models import AgentToolSelection, ToolRunLog
1717
from .tools.repo_info_tool import tool_repo_summary, RepoSummaryInput
@@ -44,10 +44,6 @@
4444
base_url=agent_model_config.base_url,
4545
api_key=api_key,
4646
)
47-
openai_model = OpenAIChatModel(
48-
model_name=agent_model_config.name,
49-
provider=provider,
50-
)
5147
else:
5248
provider = OpenAIProvider(api_key=api_key)
5349

@@ -164,51 +160,38 @@ async def search_alternative(
164160

165161
@agent.tool(retries=2, prepare=cap_prepare)
166162
@limit_tool_calls("repo_info", cap=12)
167-
async def repo_info(ctx: RunContext[AgentState], url: str, tool_name: str = None) -> dict:
163+
async def repo_info(ctx: RunContext[AgentState], url: str) -> dict:
168164
"""
169165
Fetch a short summary of a GitHub repository.
170166
171167
Non-GitHub URLs are ignored; the tool returns a small dict noting
172-
that it was skipped. If a tool_name is provided and the URL is not
173-
a GitHub URL, the tool will attempt to look up the GitHub URL from
174-
the catalog.
175-
176-
Args:
177-
url: Repository URL or GitHub owner/repo format
178-
tool_name: Optional tool name to look up in catalog if URL is not GitHub
168+
that it was skipped.
179169
"""
180170
norm_url = coerce_github_url_or_none(url)
181-
182-
# If URL is not a GitHub URL and tool_name is provided, try catalog lookup
183-
if not norm_url and tool_name:
184-
log.info(f"Non-GitHub URL provided, tool_name={tool_name}, attempting catalog lookup")
185-
# The tool_repo_summary will handle the catalog lookup
186-
norm_url = url # Pass through, tool_repo_summary will handle it
187-
elif not norm_url:
171+
if not norm_url:
188172
payload = {
189173
"tool": "repo_info",
190174
"url": url,
191175
"skipped": True,
192176
"reason": "NON_GITHUB_URL",
193-
"hint": "Pass a GitHub repo URL or 'owner/repo' to repo_info(url). Optionally provide tool_name for catalog lookup.",
177+
"hint": "Pass a GitHub repo URL or 'owner/repo' to repo_info(url).",
194178
"timestamp": datetime.now().isoformat()
195179
}
196180
ctx.deps.tool_calls.append(payload)
197181
return {k: v for k, v in payload.items() if k != "tool"}
198182

199183
try:
200-
out = await tool_repo_summary(RepoSummaryInput(url=norm_url, tool_name=tool_name))
184+
out = await tool_repo_summary(RepoSummaryInput(url=norm_url))
201185
except Exception as e:
202186
ctx.deps.tool_calls.append(
203-
{"tool": "repo_info", "url": norm_url, "tool_name": tool_name, "error": str(e), "timestamp": datetime.now().isoformat()}
187+
{"tool": "repo_info", "url": norm_url, "error": str(e), "timestamp": datetime.now().isoformat()}
204188
)
205189
raise
206190

207191
ctx.deps.tool_calls.append(
208192
{
209193
"tool": "repo_info",
210194
"url": norm_url,
211-
"tool_name": tool_name,
212195
"truncated": getattr(out, "truncated", False),
213196
"timestamp": datetime.now().isoformat()
214197
}
@@ -261,7 +244,6 @@ def run_agent(
261244
image_bytes: bytes | None = None,
262245
model: str | None = None,
263246
base_url: str | None = None,
264-
api_key_env: str | None = None,
265247
top_k: int | None = None,
266248
num_choices: int | None = None,
267249
image_metadata: str | None = None,
@@ -333,19 +315,30 @@ def run_agent(
333315

334316
# When model is provided from UI, base_url comes with it (can be None for OpenAI)
335317
if model:
336-
# Use api_key_env from config if provided, otherwise default to OPENAI_API_KEY
337-
key_env_name = api_key_env if api_key_env else "OPENAI_API_KEY"
338-
runtime_api_key = os.getenv(key_env_name)
339-
if not runtime_api_key:
340-
raise ValueError(f"{key_env_name} not found in environment. Cannot use this model.")
341-
effective_base_url = base_url # Can be None for OpenAI
342-
log.info(f"✓ Using {key_env_name} for model {effective_model}")
343-
log.debug(f"{key_env_name} starts with: {runtime_api_key[:10] if runtime_api_key else 'NONE'}... (len={len(runtime_api_key) if runtime_api_key else 0})")
318+
if base_url and "inference.rcp.epfl.ch" in base_url:
319+
runtime_api_key = os.getenv("EPFL_API_KEY")
320+
if not runtime_api_key:
321+
raise ValueError("EPFL_API_KEY not found. Cannot use EPFL models without VPN and API key.")
322+
effective_base_url = base_url
323+
log.info("✓ Using EPFL_API_KEY for EPFL inference server")
324+
else:
325+
runtime_api_key = os.getenv("OPENAI_API_KEY")
326+
if not runtime_api_key:
327+
raise ValueError("OPENAI_API_KEY not found. Cannot use OpenAI models.")
328+
effective_base_url = base_url # None for OpenAI
329+
log.info("✓ Using OPENAI_API_KEY for OpenAI endpoint")
344330
else:
345-
# No model override - use config defaults
346331
effective_base_url = agent_model_config.base_url
347-
runtime_api_key = api_key # Already loaded from config at startup
348-
log.info(f"✓ Using API key from config for model {effective_model}")
332+
if effective_base_url and "inference.rcp.epfl.ch" in effective_base_url:
333+
runtime_api_key = os.getenv("EPFL_API_KEY")
334+
if not runtime_api_key:
335+
raise ValueError("EPFL_API_KEY not found")
336+
log.info("✓ Using EPFL_API_KEY from config")
337+
else:
338+
runtime_api_key = os.getenv("OPENAI_API_KEY")
339+
if not runtime_api_key:
340+
raise ValueError("OPENAI_API_KEY not found")
341+
log.info("✓ Using OPENAI_API_KEY from config")
349342

350343
# Log runtime configuration
351344
endpoint_display = effective_base_url if effective_base_url else "api.openai.com"
@@ -369,13 +362,7 @@ def run_agent(
369362
base_url=effective_base_url,
370363
api_key=runtime_api_key,
371364
)
372-
373-
# Use OpenAIModel (chat/completions) for custom endpoints, OpenAIResponsesModel for default OpenAI
374-
if effective_base_url:
375-
log.info("Using OpenAIChatModel (chat/completions API) for custom endpoint")
376-
runtime_model = OpenAIChatModel(model_name=effective_model, provider=runtime_provider)
377-
else:
378-
runtime_model = OpenAIResponsesModel(model_name=effective_model, provider=runtime_provider)
365+
runtime_model = OpenAIResponsesModel(model_name=effective_model, provider=runtime_provider)
379366

380367
agent_instance = Agent(
381368
model=runtime_model,
@@ -429,51 +416,27 @@ def run_agent(
429416
user_prompt = prompt
430417

431418
# ---- 6) Run the agent --------------------------------------------------
432-
try:
433-
run_result = agent_instance.run_sync(
434-
user_prompt,
435-
deps=deps,
436-
output_type=ToolSelection,
437-
usage_limits=UsageLimits(tool_calls_limit=20),
438-
)
439-
result = run_result.output
440-
441-
log.info(f"✅ Agent execution complete - choices returned: {len(result.choices)}")
419+
run_result = agent_instance.run_sync(
420+
user_prompt,
421+
deps=deps,
422+
output_type=ToolSelection,
423+
usage_limits=UsageLimits(tool_calls_limit=20),
424+
)
425+
result = run_result.output
442426

443-
# Log usage (helpful, but may not explicitly expose image-specific counters)
444-
if run_result.usage:
445-
usage = run_result.usage()
446-
log.info(
447-
f"📊 Usage: total_tokens={usage.total_tokens}, "
448-
f"input_tokens={usage.input_tokens}, output_tokens={usage.output_tokens}"
449-
)
427+
log.info(f"✅ Agent execution complete - choices returned: {len(result.choices)}")
450428

451-
# Warn if using non-OpenAI endpoint with images
452-
if image_bytes and effective_base_url:
453-
log.warning("⚠️ Using custom endpoint - confirm the selected model supports vision.")
429+
# Log usage (helpful, but may not explicitly expose image-specific counters)
430+
if run_result.usage:
431+
usage = run_result.usage()
432+
log.info(
433+
f"📊 Usage: total_tokens={usage.total_tokens}, "
434+
f"request_tokens={usage.request_tokens}, response_tokens={usage.response_tokens}"
435+
)
454436

455-
except Exception as e:
456-
# Handle global tool quota limit (UsageLimitExceeded) and other errors gracefully
457-
error_msg = str(e)
458-
log.warning(f"⚠️ Agent execution encountered an error: {error_msg}")
459-
460-
# Check if this is a usage limit error (global tool quota)
461-
if "UsageLimitExceeded" in str(type(e).__name__) or "tool_calls_limit" in error_msg.lower():
462-
log.warning("Global tool call quota reached - continuing with partial results")
463-
464-
result = ToolSelection(
465-
conversation=Conversation(
466-
status=ConversationStatus.COMPLETE,
467-
context="The agent reached the maximum number of tool calls allowed. Please try a more specific query or break down your request into smaller parts.",
468-
question=None,
469-
options=None
470-
),
471-
choices=[],
472-
explanation="Tool call limit reached during execution. Try refining your query.",
473-
reason=None
474-
)
475-
else:
476-
raise
437+
if image_bytes and ("inference.rcp.epfl.ch" in endpoint_display):
438+
log.warning("⚠️ Using EPFL inference server - confirm the selected model supports vision on that endpoint.")
439+
log.warning(" OpenAI billing/dashboard may not reflect image usage when using a non-OpenAI endpoint.")
477440

478441
# ---- 7) Convert raw tool call records into ToolRunLog objects ----------
479442
for tc in getattr(deps, "tool_calls", []):

src/ai_agent/ui/components.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,33 @@
1010

1111
from .handlers import respond
1212
from .visualizations import create_tool_usage_chart, create_tool_timeline, create_disabled_tools_display
13-
from .utils import get_available_models, get_default_ui_model
1413

1514
log = logging.getLogger("chat_components")
1615

16+
# Model configurations with their inference servers
17+
MODEL_CONFIGS = {
18+
# OpenAI models (default endpoint)
19+
"gpt-4o-mini": {"name": "gpt-4o-mini", "base_url": None, "provider": "OpenAI"},
20+
"gpt-4o": {"name": "gpt-4o", "base_url": None, "provider": "OpenAI"},
21+
"gpt-4-turbo": {"name": "gpt-4-turbo", "base_url": None, "provider": "OpenAI"},
22+
23+
# EPFL inference server models
24+
"openai/gpt-oss-120b [EPFL]": {
25+
"name": "openai/gpt-oss-120b",
26+
"base_url": "https://inference-rcp.epfl.ch/v1",
27+
"provider": "EPFL"
28+
},
29+
"mistralai/Mistral-Small-3.2-24B-Instruct-2506 [EPFL]": {
30+
"name": "mistralai/Mistral-Small-3.2-24B-Instruct-2506",
31+
"base_url": "https://inference.rcp.epfl.ch/v1",
32+
"provider": "EPFL"
33+
},
34+
}
35+
36+
def get_model_config(model_display_name: str) -> Dict[str, str]:
37+
"""Get model configuration from display name."""
38+
return MODEL_CONFIGS.get(model_display_name, {"name": model_display_name, "base_url": None, "provider": "Unknown"})
39+
1740

1841
def create_chat_interface(doc_index: Dict[str, SoftwareDoc]):
1942
"""
@@ -102,7 +125,7 @@ def create_chat_interface(doc_index: Dict[str, SoftwareDoc]):
102125
with gr.Row(elem_classes="main-header"):
103126
gr.HTML("""
104127
<div class="logo-container">
105-
<img src="https://imaging-plaza.epfl.ch/logos/imaging_plaza.svg"
128+
<img src="https://imaging-plaza.epfl.ch/logos/imaging_plaza_white.svg"
106129
alt="Imaging Plaza Logo"
107130
style="height: 48px; width: auto;" />
108131
<div>
@@ -115,14 +138,9 @@ def create_chat_interface(doc_index: Dict[str, SoftwareDoc]):
115138
# Settings section (collapsed by default)
116139
with gr.Accordion("⚙️ Settings", open=False):
117140
with gr.Row():
118-
# Load models and default from config
119-
available_models = get_available_models()
120-
model_choices = [m["display_name"] for m in available_models]
121-
default_model = get_default_ui_model()
122-
123141
model_dropdown = gr.Dropdown(
124-
choices=model_choices,
125-
value=default_model,
142+
choices=list(MODEL_CONFIGS.keys()),
143+
value="gpt-4o-mini",
126144
label="Model",
127145
info="Select AI model and inference server",
128146
)

src/ai_agent/ui/handlers.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
from .state import ChatState, ChatMessage
1717
from .formatters import format_tool_card
18-
from .utils import get_model_config
1918

2019
log = logging.getLogger("chat_handlers")
2120

@@ -202,13 +201,13 @@ def respond(
202201
# Parse model configuration if provided
203202
model_name = None
204203
base_url_override = None # Use different variable name
205-
api_key_env = None
206204
if model:
205+
# Import here to avoid circular dependency
206+
from ai_agent.ui.components import get_model_config
207207
model_config = get_model_config(model)
208208
model_name = model_config.get("name")
209209
base_url_override = model_config.get("base_url") # Can be None for OpenAI
210-
api_key_env = model_config.get("api_key_env", "OPENAI_API_KEY")
211-
log.info(f"Model config: {model} -> name={model_name}, base_url={base_url_override}, api_key_env={api_key_env}")
210+
log.info(f"Model config: {model} -> name={model_name}, base_url={base_url_override}")
212211

213212
effective_paths = file_paths or (state.last_files or [])
214213

@@ -229,7 +228,7 @@ def respond(
229228
conversation_history=state.conversation_history,
230229
model=model_name,
231230
base_url=base_url_override if model else None, # Only override if model selected
232-
api_key_env=api_key_env,
231+
top_k=top_k,
233232
num_choices=num_choices,
234233
)
235234
except ValueError as e:

0 commit comments

Comments
 (0)