Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ OPENCAD_AGENT_LIVE_KERNEL=false
OPENCAD_TREE_LIVE_KERNEL=true
OPENCAD_AGENT_LIVE_KERNEL=true

OPENCAD_LLM_PROVIDER=
OPENCAD_LLM_MODEL=

# Optional multi-provider LLM support for code generation uses LiteLLM.
# Provider-specific credentials are still read from each provider's standard env vars.
# Example request fields: llm_provider="openai", llm_model="gpt-4o-mini", generate_code=true
12 changes: 3 additions & 9 deletions backend/opencad_agent/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@ def _default_completion(**kwargs: Any) -> Any:
return completion(**kwargs)


def _resolve_model_name(provider: str | None, model: str) -> str:
if provider and "/" not in model:
return f"{provider}/{model}"
return model


def _strip_code_fences(code: str) -> str:
code = code.strip()
if code.startswith("```python"):
Expand Down Expand Up @@ -68,7 +62,6 @@ def __init__(self, completion_func: LiteLlmCompletion | None = None) -> None:
def generate_code(
self,
*,
provider: str | None,
model: str,
system_prompt: str,
user_message: str,
Expand All @@ -79,9 +72,10 @@ def generate_code(
messages.extend({"role": item.role, "content": item.content} for item in conversation_history)
messages.append({"role": "user", "content": user_message})
response = self._completion(
model=_resolve_model_name(provider, model),
model=model,
messages=messages,
temperature=HIGH_REASONING_CODE_TEMPERATURE if reasoning else DEFAULT_CODE_TEMPERATURE,
)
logger.debug("Received LLM code-generation response")
return _strip_code_fences(_extract_message_content(response))
cleaned_response = _strip_code_fences(_extract_message_content(response))
return cleaned_response
7 changes: 0 additions & 7 deletions backend/opencad_agent/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,8 @@ class ChatRequest(BaseModel):
tree_state: FeatureTree
conversation_history: list[ChatHistoryItem] = Field(default_factory=list)
reasoning: bool = False
llm_provider: str | None = None
llm_model: str | None = None
generate_code: bool = False

@model_validator(mode="after")
def _validate_llm_configuration(self) -> ChatRequest:
if self.llm_provider and not self.llm_model:
raise ValueError("llm_model is required when llm_provider is set.")
return self


class OperationExecution(BaseModel):
Expand Down
5 changes: 2 additions & 3 deletions backend/opencad_agent/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,10 @@ def _execute_code_in_context(code: str, ctx: object) -> None:
reset_default_context()

def _generate_code(self, request: ChatRequest) -> str:
provider = request.llm_provider or os.environ.get("OPENCAD_LLM_PROVIDER")
model = request.llm_model or os.environ.get("OPENCAD_LLM_MODEL")
model = os.environ.get("OPENCAD_LLM_MODEL")

if model:
return self.llm_client.generate_code(
provider=provider,
model=model,
system_prompt=build_code_generation_prompt(request.tree_state),
user_message=request.message,
Expand Down
Loading