Skip to content

Commit 2950f2f

Browse files
committed
Merge branch 'development' of github.com:enso-labs/orchestra into development
2 parents 826240c + 06f87e3 commit 2950f2f

30 files changed

Lines changed: 566 additions & 366 deletions

File tree

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1919
- bug/370-anthropic-streaming (2025-09-16)
2020

2121
### Changed
22+
- feat/464-update-input-support-multiple-keys (2025-11-11)
2223
- feat/490-can-read-deepagents-agent-files (2025-11-10)
2324
- feat/485-user-env (2025-11-08)
2425
- feat/482-teams-webhook (2025-11-05)

backend/src/constants/llm.py

Lines changed: 84 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,97 @@
1-
from enum import Enum
21
import os
2+
from enum import Enum
3+
from src.services.llm import llm_service
34
from src.constants import (
4-
OPENAI_API_KEY,
5-
ANTHROPIC_API_KEY,
6-
OLLAMA_BASE_URL,
7-
GROQ_API_KEY,
8-
GOOGLE_API_KEY,
9-
XAI_API_KEY,
5+
OPENAI_API_KEY,
6+
ANTHROPIC_API_KEY,
7+
OLLAMA_BASE_URL,
8+
GROQ_API_KEY,
9+
GOOGLE_API_KEY,
10+
XAI_API_KEY,
1011
)
12+
from src.utils.logger import logger
1113

1214

1315
class ChatModels(str, Enum):
14-
if OPENAI_API_KEY:
15-
OPENAI_REASONING_03 = "openai:o3"
16-
OPENAI_REASONING_04_MINI = "openai:o4-mini"
17-
OPENAI_GPT_5_NANO = "openai:gpt-5-nano"
18-
OPENAI_GPT_5_MINI = "openai:gpt-5-mini"
19-
OPENAI_GPT_5 = "openai:gpt-5"
20-
# OPENAI_GPT_5_CODEX = "openai:gpt-5-codex"
21-
if ANTHROPIC_API_KEY:
22-
ANTHROPIC_CLAUDE_3_7_SONNET = "anthropic:claude-3-7-sonnet-latest"
23-
ANTHROPIC_CLAUDE_4_SONNET = "anthropic:claude-sonnet-4"
24-
ANTHROPIC_CLAUDE_4_OPUS = "anthropic:claude-opus-4-1"
25-
ANTHROPIC_CLAUDE_4_5_HAIKU = "anthropic:claude-haiku-4-5"
26-
ANTHROPIC_CLAUDE_4_5_SONNET = "anthropic:claude-sonnet-4-5"
27-
if XAI_API_KEY:
28-
XAI_GROK_4 = "xai:grok-4"
29-
XAI_GROK_4_FAST = "xai:grok-4-fast"
30-
XAI_GROK_4_FAST_NON_REASONING = "xai:grok-4-fast-non-reasoning"
31-
XAI_GROK_CODE_FAST_1 = "xai:grok-code-fast-1"
32-
if GOOGLE_API_KEY:
33-
GOOGLE_GEMINI_2_5_FLASH_LITE = "google_genai:gemini-2.5-flash-lite"
34-
GOOGLE_GEMINI_2_5_FLASH = "google_genai:gemini-2.5-flash"
35-
GOOGLE_GEMINI_2_5_PRO = "google_genai:gemini-2.5-pro"
36-
if GROQ_API_KEY:
37-
GROQ_OPENAI_GPT_OSS_120B = "groq:openai/gpt-oss-120b"
38-
GROQ_LLAMA_3_3_70B_VERSATILE = "groq:llama-3.3-70b-versatile"
39-
if OLLAMA_BASE_URL:
40-
OLLAMA_QWEN3 = "ollama:qwen3"
41-
16+
if OPENAI_API_KEY:
17+
OPENAI_REASONING_03 = "openai:o3"
18+
OPENAI_REASONING_04_MINI = "openai:o4-mini"
19+
OPENAI_GPT_5_NANO = "openai:gpt-5-nano"
20+
OPENAI_GPT_5_MINI = "openai:gpt-5-mini"
21+
OPENAI_GPT_5 = "openai:gpt-5"
22+
# OPENAI_GPT_5_CODEX = "openai:gpt-5-codex"
23+
if ANTHROPIC_API_KEY:
24+
ANTHROPIC_CLAUDE_3_7_SONNET = "anthropic:claude-3-7-sonnet-latest"
25+
ANTHROPIC_CLAUDE_4_SONNET = "anthropic:claude-sonnet-4"
26+
ANTHROPIC_CLAUDE_4_OPUS = "anthropic:claude-opus-4-1"
27+
ANTHROPIC_CLAUDE_4_5_HAIKU = "anthropic:claude-haiku-4-5"
28+
ANTHROPIC_CLAUDE_4_5_SONNET = "anthropic:claude-sonnet-4-5"
29+
if XAI_API_KEY:
30+
XAI_GROK_4 = "xai:grok-4"
31+
XAI_GROK_4_FAST = "xai:grok-4-fast"
32+
XAI_GROK_4_FAST_NON_REASONING = "xai:grok-4-fast-non-reasoning"
33+
XAI_GROK_CODE_FAST_1 = "xai:grok-code-fast-1"
34+
if GOOGLE_API_KEY:
35+
GOOGLE_GEMINI_2_5_FLASH_LITE = "google_genai:gemini-2.5-flash-lite"
36+
GOOGLE_GEMINI_2_5_FLASH = "google_genai:gemini-2.5-flash"
37+
GOOGLE_GEMINI_2_5_PRO = "google_genai:gemini-2.5-pro"
38+
if GROQ_API_KEY:
39+
GROQ_OPENAI_GPT_OSS_120B = "groq:openai/gpt-oss-120b"
40+
GROQ_LLAMA_3_3_70B_VERSATILE = "groq:llama-3.3-70b-versatile"
41+
42+
43+
def get_ollama_models():
44+
models = []
45+
try:
46+
import requests
47+
response = requests.get(f"{OLLAMA_BASE_URL.rstrip('/')}/api/tags", timeout=3)
48+
if response.ok:
49+
data = response.json()
50+
tags = data.get("models", []) if isinstance(data, dict) else []
51+
for tag in tags:
52+
model_name = tag.get("name")
53+
if model_name:
54+
models.append(f"ollama:{model_name}")
55+
except Exception as e:
56+
logger.error(f"Error getting Ollama models: {e}")
57+
pass
58+
return models
59+
60+
def get_all_models():
61+
models = []
62+
if OPENAI_API_KEY:
63+
models.extend(llm_service.model_by_provider(provider="openai"))
64+
if ANTHROPIC_API_KEY:
65+
models.extend(llm_service.model_by_provider(provider="anthropic"))
66+
if GOOGLE_API_KEY:
67+
models.extend(llm_service.model_by_provider(provider="google"))
68+
if GROQ_API_KEY:
69+
models.extend(llm_service.model_by_provider(provider="groq"))
70+
if XAI_API_KEY:
71+
models.extend(llm_service.model_by_provider(provider="xai"))
72+
if OLLAMA_BASE_URL:
73+
models.extend(get_ollama_models())
74+
return sorted(models)
75+
4276
def get_free_models():
43-
return [
44-
ChatModels.ANTHROPIC_CLAUDE_4_5_HAIKU.value,
45-
ChatModels.OPENAI_GPT_5_NANO.value,
46-
ChatModels.GOOGLE_GEMINI_2_5_FLASH_LITE.value,
47-
ChatModels.GROQ_OPENAI_GPT_OSS_120B.value,
48-
ChatModels.GROQ_LLAMA_3_3_70B_VERSATILE.value,
49-
]
77+
models = []
78+
if OPENAI_API_KEY:
79+
models.append(ChatModels.OPENAI_GPT_5_NANO.value)
80+
if ANTHROPIC_API_KEY:
81+
models.append(ChatModels.ANTHROPIC_CLAUDE_4_5_HAIKU.value)
82+
if GOOGLE_API_KEY:
83+
models.append(ChatModels.GOOGLE_GEMINI_2_5_FLASH_LITE.value)
84+
if GROQ_API_KEY:
85+
models.append(ChatModels.GROQ_OPENAI_GPT_OSS_120B.value)
86+
if OLLAMA_BASE_URL:
87+
models.extend(get_ollama_models())
88+
return sorted(models)
5089

5190

5291
def get_system_prompt():
53-
path = "src/static/prompts/md"
54-
with open(os.path.join(path, "default.md"), "r") as file:
55-
return file.read()
92+
path = "src/static/prompts/md"
93+
with open(os.path.join(path, "default.md"), "r") as file:
94+
return file.read()
5695

5796

5897
DEFAULT_SYSTEM_PROMPT = get_system_prompt()

backend/src/flows/__init__.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@
1212
from deepagents import SubAgent, create_deep_agent
1313

1414

15+
from src.schemas.models.auth import ProtectedUser
1516
from src.services.memory import memory_service
1617
from src.services.tool import tool_service
1718
from src.tools.memory import MEMORY_TOOLS
18-
from src.schemas.entities import LLMRequest, LLMStreamRequest
19+
from src.schemas.entities import LLMRequest
1920
from src.utils.logger import logger
2021
from src.utils.format import init_system_prompt
2122
from src.schemas.contexts import ContextSchema
2223
from src.schemas.entities.a2a import A2AServers
24+
from src.utils.middleware import add_ai_message_metadata, pii_middleware
2325

2426

2527
async def add_memories_to_system():
@@ -50,7 +52,7 @@ def graph_builder(
5052
subagents: list[SubAgent] = [],
5153
prompt: str = "You are a helpful assistant.",
5254
model: str = "openai:gpt-5-nano",
53-
context_schema: Type[Any] | None = None,
55+
context_schema: Type[ContextSchema] | None = None,
5456
checkpointer: BaseCheckpointSaver | None = None,
5557
store: BaseStore | None = None,
5658
graph_id: Literal[
@@ -74,6 +76,7 @@ def graph_builder(
7476
system_prompt=prompt,
7577
checkpointer=checkpointer,
7678
context_schema=context_schema,
79+
middleware=[add_ai_message_metadata] + pii_middleware(),
7780
store=store,
7881
)
7982
return deep_agent
@@ -96,7 +99,7 @@ async def init_tools(
9699
return tools
97100

98101

99-
async def init_subagents(params: LLMRequest | LLMStreamRequest) -> list[SubAgent]:
102+
async def init_subagents(params: LLMRequest) -> list[SubAgent]:
100103
result = []
101104
for subagent in params.subagents:
102105
subagent_dict = {
@@ -120,22 +123,28 @@ async def init_memories(system_prompt: str, tools: list[BaseTool]):
120123
return tools + MEMORY_TOOLS, prompt
121124

122125

123-
def init_config(params: LLMRequest | LLMStreamRequest):
124-
if params.metadata:
125-
return RunnableConfig(
126-
configurable=params.metadata.model_dump(),
127-
max_concurrency=10,
128-
recursion_limit=100,
129-
)
130-
else:
131-
return None
126+
def init_config(
127+
params: LLMRequest,
128+
user: ProtectedUser | None = None,
129+
max_concurrency: int = 4,
130+
recursion_limit: int = 100,
131+
) -> RunnableConfig:
132+
return RunnableConfig(
133+
configurable={
134+
"user_id": user.id if user else None,
135+
"thread_id": params.metadata.thread_id or str(uuid4()),
136+
"assistant_id": params.metadata.assistant_id or None,
137+
},
138+
max_concurrency=max_concurrency,
139+
recursion_limit=recursion_limit,
140+
metadata={**params.metadata.model_dump()},
141+
)
132142

133143

134144
################################################################################
135145
### Construct Agent
136146
################################################################################
137147
async def construct_agent(
138-
# params: LLMRequest | LLMStreamRequest,
139148
system_prompt: str,
140149
tools: list[BaseTool],
141150
model: BaseChatModel,
@@ -218,10 +227,4 @@ def astream(
218227
) -> AsyncGenerator[BaseMessage, None]:
219228
return self.graph.astream(
220229
messages, config=config, stream_mode=stream_mode, context=context
221-
)
222-
223-
async def aget_state(self, config: RunnableConfig = None):
224-
# if config is None:
225-
# config = self.config
226-
state = await self.graph.aget_state(config)
227-
return state
230+
)

backend/src/routes/v0/assistant.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from src.utils.auth import verify_credentials
1111
from src.utils.logger import logger
1212
from src.services.assistant import (
13-
assistant_service,
1413
AssistantSearch,
1514
Assistant,
1615
ASSISTANT_EXAMPLES,

0 commit comments

Comments
 (0)