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
130 changes: 73 additions & 57 deletions flocks/agent/agent_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,68 +110,84 @@ def load_agent(agent_dir: Path, native: bool = False) -> Optional[AgentInfo]:
})
return None

if not isinstance(raw, dict):
log.warn("agent.factory.yaml_invalid", {
"path": str(yaml_path),
"hint": "Expected a YAML mapping",
})
return None

name = raw.get("name") or agent_dir.name
if not name:
log.warn("agent.factory.missing_name", {"path": str(yaml_path)})
return None

# ── Prompt resolution ──────────────────────────────────────────────────
prompt: Optional[str] = None
prompt_builder: Optional[str] = None

prompt_md = agent_dir / "prompt.md"
prompt_builder_py = agent_dir / "prompt_builder.py"

if prompt_md.is_file():
prompt = prompt_md.read_text(encoding="utf-8").strip()
elif prompt_builder_py.is_file():
# Derive Python module path from file location, relative to flocks package root
try:
rel = prompt_builder_py.relative_to(Path(__file__).parent.parent.parent)
module_path = str(rel.with_suffix("")).replace("/", ".").replace("\\", ".")
except ValueError:
# Fallback: use absolute path notation
module_path = str(prompt_builder_py)
prompt_builder = f"{module_path}:inject"

# ── Tools / legacy permission compatibility ─────────────────────────────
tools_list_raw: Optional[List[str]] = raw.get("tools")
perm_raw = raw.get("permission")
tools_list, legacy_permission = resolve_agent_initial_tools(
tools_list_raw,
perm_raw,
agent_name=name,
)

# ── Model ────────────────────────────────────────────────────────────────
model_raw = raw.get("model")
model = AgentModel(**model_raw) if isinstance(model_raw, dict) else None

desc_cn = raw.get("description_cn")
if desc_cn is None and isinstance(raw.get("descriptionCn"), str):
desc_cn = raw.get("descriptionCn")

return AgentInfo(
name=name,
description=raw.get("description"),
description_cn=desc_cn,
mode=raw.get("mode", "subagent"),
native=native,
hidden=raw.get("hidden", False),
color=raw.get("color"),
permission=legacy_permission,
model=model,
prompt=prompt,
prompt_builder=prompt_builder,
tools=tools_list,
options=raw.get("options", {}),
steps=raw.get("steps"),
delegatable=raw.get("delegatable"),
temperature=raw.get("temperature"),
top_p=raw.get("top_p"),
prompt_metadata=_parse_prompt_metadata(raw),
tags=raw.get("tags", []),
)
try:
# ── Prompt resolution ──────────────────────────────────────────────
prompt: Optional[str] = None
prompt_builder: Optional[str] = None

prompt_md = agent_dir / "prompt.md"
prompt_builder_py = agent_dir / "prompt_builder.py"

if prompt_md.is_file():
prompt = prompt_md.read_text(encoding="utf-8").strip()
elif prompt_builder_py.is_file():
# Derive Python module path from file location, relative to flocks package root
try:
rel = prompt_builder_py.relative_to(Path(__file__).parent.parent.parent)
module_path = str(rel.with_suffix("")).replace("/", ".").replace("\\", ".")
except ValueError:
# Fallback: use absolute path notation
module_path = str(prompt_builder_py)
prompt_builder = f"{module_path}:inject"

# ── Tools / legacy permission compatibility ─────────────────────────
tools_list_raw: Optional[List[str]] = raw.get("tools")
perm_raw = raw.get("permission")
tools_list, legacy_permission = resolve_agent_initial_tools(
tools_list_raw,
perm_raw,
agent_name=name,
)

# ── Model ───────────────────────────────────────────────────────────
model_raw = raw.get("model")
model = AgentModel(**model_raw) if isinstance(model_raw, dict) else None

desc_cn = raw.get("description_cn")
if desc_cn is None and isinstance(raw.get("descriptionCn"), str):
desc_cn = raw.get("descriptionCn")

return AgentInfo(
name=name,
description=raw.get("description"),
description_cn=desc_cn,
mode=raw.get("mode", "subagent"),
native=native,
hidden=raw.get("hidden", False),
color=raw.get("color"),
permission=legacy_permission,
model=model,
prompt=prompt,
prompt_builder=prompt_builder,
tools=tools_list,
options=raw.get("options", {}),
steps=raw.get("steps"),
delegatable=raw.get("delegatable"),
temperature=raw.get("temperature"),
top_p=raw.get("top_p"),
prompt_metadata=_parse_prompt_metadata(raw),
tags=raw.get("tags", []),
)
except Exception as e:
log.error("agent.factory.load_failed", {
"name": name,
"path": str(yaml_path),
"error": str(e),
"type": type(e).__name__,
})
return None


# ---------------------------------------------------------------------------
Expand Down
100 changes: 100 additions & 0 deletions flocks/provider/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
"family": "minimax",
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_details",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -74,6 +80,12 @@
"family": "minimax",
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_details",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -92,6 +104,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -110,6 +127,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -128,6 +150,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -146,6 +173,12 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"placeholder": " ",
"cross_provider_policy": "placeholder"
},
"supports_streaming": true
},
"limits": {
Expand Down Expand Up @@ -190,6 +223,12 @@
"family": "minimax",
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_details",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -207,6 +246,12 @@
"family": "minimax",
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_details",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -225,6 +270,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -243,6 +293,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -261,6 +316,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand Down Expand Up @@ -893,6 +953,12 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"placeholder": " ",
"cross_provider_policy": "placeholder"
},
"supports_streaming": true
},
"limits": {
Expand Down Expand Up @@ -1027,6 +1093,12 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"placeholder": " ",
"cross_provider_policy": "placeholder"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -1047,6 +1119,12 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"placeholder": " ",
"cross_provider_policy": "placeholder"
},
"supports_streaming": true
},
"limits": {
Expand Down Expand Up @@ -1114,6 +1192,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand Down Expand Up @@ -1149,6 +1232,11 @@
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_content",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand Down Expand Up @@ -1198,6 +1286,12 @@
"family": "minimax-m2",
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_details",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand All @@ -1215,6 +1309,12 @@
"family": "minimax-m2",
"capabilities": {
"supports_tools": true,
"supports_reasoning": true,
"interleaved": {
"field": "reasoning_details",
"echo": "tool_calls",
"cross_provider_policy": "promote"
},
"supports_streaming": true
},
"limits": {
Expand Down
Loading