Initial project structure
Scaffold all modules, route stubs, data models, and config. No logic implemented yet — all core methods raise NotImplementedError. Establishes the full directory layout matching the architecture in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
92
fellowship/core/orchestrator.py
Normal file
92
fellowship/core/orchestrator.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""
|
||||
Orchestrator — stateless LLM call that selects the next speaker or ends the session.
|
||||
Called fresh each turn when turn_order is ORCHESTRATED.
|
||||
Output is a tool call only; any text is discarded.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal, Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from fellowship.core.session import Session
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class OrchestratorDecision:
|
||||
action: Literal["select_speaker", "hold", "end_session"]
|
||||
bot_name: Optional[str] = None # set when action == "select_speaker"
|
||||
reason: Optional[str] = None # set when action == "end_session"
|
||||
|
||||
|
||||
# Tool definitions sent to the LLM with every orchestrator call
|
||||
ORCHESTRATOR_TOOLS = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "select_speaker",
|
||||
"description": "Choose which bot should speak next.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"bot_name": {"type": "string", "description": "Name of the bot to speak next"},
|
||||
},
|
||||
"required": ["bot_name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "hold",
|
||||
"description": (
|
||||
"Do not prompt any bot this turn. "
|
||||
"Use when the conversation implies bots should stay silent."
|
||||
),
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "end_session",
|
||||
"description": "End the session. Only use when the session goal has been reached.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"reason": {"type": "string", "description": "Why the session is ending"},
|
||||
},
|
||||
"required": ["reason"],
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class Orchestrator:
|
||||
def __init__(self, session: "Session") -> None:
|
||||
self.session = session
|
||||
|
||||
async def decide(self) -> OrchestratorDecision:
|
||||
"""
|
||||
Build the orchestrator prompt, call the LLM, parse the tool call response.
|
||||
Any text output from the LLM is ignored — only the tool call matters.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _build_system_prompt(self) -> str:
|
||||
"""
|
||||
Build the orchestrator system prompt including:
|
||||
- Its role and instructions
|
||||
- Overview of how Fellowship works
|
||||
- Full bot roster (names, roles, system prompts)
|
||||
- Session goal (if set)
|
||||
- Instruction to always respond with a tool call
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _parse_tool_call(self, response: dict) -> OrchestratorDecision:
|
||||
"""Parse the LLM tool call response into an OrchestratorDecision."""
|
||||
raise NotImplementedError
|
||||
Reference in New Issue
Block a user