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
11 changes: 9 additions & 2 deletions examples/spinner.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ def build_spin(intent: SpinnerIntent, context: BuildContext) -> Briefing:

class FakeClassifier:
def classify(
self, message: str, *, context: Continuation | None = None,
self,
message: str,
*,
context: Continuation | None = None,
) -> SpinnerIntent:
del context
action = "spin" if "spin" in message.lower() else "chat"
Expand All @@ -67,7 +70,11 @@ def classify(

class SpinnerRuntime:
def execute(
self, message: str, plan: ExecutionPlan, *, context: object | None = None,
self,
message: str,
plan: ExecutionPlan,
*,
context: object | None = None,
) -> Completed:
del message, context
if "spin_wheel" not in plan.envelope.allowed:
Expand Down
25 changes: 6 additions & 19 deletions src/agent_kernel/core/authority.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@ def __init__(
def _validate_grants(spec: ActionSpec[I], briefing: Briefing) -> None:
undeclared = briefing.grants - spec.capabilities
if undeclared:
raise ConfigurationError(
f"action {spec.name!r} built undeclared grants: {sorted(undeclared)}"
)
raise ConfigurationError(f"action {spec.name!r} built undeclared grants: {sorted(undeclared)}")
if spec.kind == "toolfree" and briefing.grants:
raise ConfigurationError(
f"toolfree action {spec.name!r} built grants: {sorted(briefing.grants)}"
)
raise ConfigurationError(f"toolfree action {spec.name!r} built grants: {sorted(briefing.grants)}")

def plan(self, intent: I, principal: Principal) -> ExecutionPlan:
intent, valid = self.registry.normalize_intent(intent)
Expand All @@ -46,14 +42,8 @@ def plan(self, intent: I, principal: Principal) -> ExecutionPlan:
elif spec is None:
reason = f"unknown action: {intent.action}"
spec = self.registry.get(self.registry.default)
elif (
spec.kind == "privileged"
and intent.confidence < self.confidence_threshold
):
reason = (
f"confidence {intent.confidence:.2f} "
f"< {self.confidence_threshold:.2f}"
)
elif spec.kind == "privileged" and intent.confidence < self.confidence_threshold:
reason = f"confidence {intent.confidence:.2f} < {self.confidence_threshold:.2f}"
spec = self.registry.get(self.registry.default)
elif spec.kind == "privileged" and spec.continuation is not None:
for field_name, allowed_values in spec.continuation.authority_fields.items():
Expand Down Expand Up @@ -82,8 +72,7 @@ def plan(self, intent: I, principal: Principal) -> ExecutionPlan:

if not briefing.grants <= principal.role.capabilities:
raise ConfigurationError(
f"final grants exceed principal authority: "
f"{sorted(briefing.grants - principal.role.capabilities)}"
f"final grants exceed principal authority: {sorted(briefing.grants - principal.role.capabilities)}"
)

return ExecutionPlan(
Expand All @@ -92,9 +81,7 @@ def plan(self, intent: I, principal: Principal) -> ExecutionPlan:
capabilities=briefing.grants,
instructions=briefing.instructions,
tool_call_limit=(
briefing.tool_call_limit
if briefing.tool_call_limit is not None
else spec.tool_call_limit
briefing.tool_call_limit if briefing.tool_call_limit is not None else spec.tool_call_limit
),
reads_history=spec.reads_history,
envelope=AuthorityEnvelope(principal.id, briefing.grants),
Expand Down
11 changes: 2 additions & 9 deletions src/agent_kernel/core/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,12 @@ def describe(
) -> tuple[dict[str, str | None], ...]:
visible: list[CatalogEntry] = []
for spec in planner.registry.specs:
if (
spec.kind == "privileged"
and not spec.capabilities <= principal.role.capabilities
):
if spec.kind == "privileged" and not spec.capabilities <= principal.role.capabilities:
continue
visible.extend(spec.catalog)

visible_keys = {entry.key for entry in visible}
current = (
entry
for entry in visible
if entry.superseded_by is None or entry.superseded_by not in visible_keys
)
current = (entry for entry in visible if entry.superseded_by is None or entry.superseded_by not in visible_keys)
return tuple(
{
"key": entry.key,
Expand Down
4 changes: 1 addition & 3 deletions src/agent_kernel/core/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ def parse_command(message: str, registry: Registry[I]) -> I | None:
return None

matches: list[CommandSpec[I]] = [
spec.command
for spec in registry.specs
if spec.command is not None and spec.command.name == name
spec.command for spec in registry.specs if spec.command is not None and spec.command.name == name
]
if len(matches) != 1:
return None
Expand Down
4 changes: 1 addition & 3 deletions src/agent_kernel/core/continuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ def from_turn(
if continuation_spec is None:
return cls(last_action=spec.name)

bounded_awaiting = (
awaiting if awaiting in continuation_spec.awaiting else None
)
bounded_awaiting = awaiting if awaiting in continuation_spec.awaiting else None
authority_values = []
for field_name, allowed_values in continuation_spec.authority_fields.items():
value = getattr(intent, field_name, None)
Expand Down
4 changes: 1 addition & 3 deletions src/agent_kernel/core/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ def __post_init__(self) -> None:
string_tuple(self.instructions, field="execution plan instructions"),
)
if capabilities != self.envelope.allowed:
raise ConfigurationError(
"execution plan capabilities must equal authority envelope allowed"
)
raise ConfigurationError("execution plan capabilities must equal authority envelope allowed")


@dataclass(frozen=True)
Expand Down
53 changes: 12 additions & 41 deletions src/agent_kernel/core/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ def __post_init__(self) -> None:
snapshot: dict[str, frozenset[str]] = {}
for field_name, values in self.authority_fields.items():
if not isinstance(field_name, str) or not field_name.strip():
raise ConfigurationError(
"continuation authority fields must be non-empty strings"
)
raise ConfigurationError("continuation authority fields must be non-empty strings")
snapshot[field_name] = capability_set(
values,
field="continuation authority values",
Expand Down Expand Up @@ -122,33 +120,18 @@ def __init__(self, specs: tuple[ActionSpec[I], ...], *, default: str, denied: st
for spec in self._specs.values():
if spec.continuation is None:
continue
for field_name, declared_values in (
spec.continuation.authority_fields.items()
):
for field_name, declared_values in spec.continuation.authority_fields.items():
model_field = intent_type.model_fields.get(field_name)
if (
field_name in {"action", "confidence", "brief"}
or model_field is None
or not declared_values
):
raise ConfigurationError(
f"invalid authority vocabulary for field {field_name!r}"
)
authority_values.setdefault(field_name, set()).update(
declared_values
)
if field_name in {"action", "confidence", "brief"} or model_field is None or not declared_values:
raise ConfigurationError(f"invalid authority vocabulary for field {field_name!r}")
authority_values.setdefault(field_name, set()).update(declared_values)
authority_fields = tuple(sorted(authority_values))
authority_options = tuple(
tuple(sorted(authority_values[field_name]))
for field_name in authority_fields
)
authority_options = tuple(tuple(sorted(authority_values[field_name])) for field_name in authority_fields)
candidate_count = 1
for options in authority_options:
candidate_count *= len(options)
if candidate_count > _MAX_FALLBACK_CANDIDATES:
raise ConfigurationError(
"authority vocabularies produce too many fallback candidates"
)
raise ConfigurationError("authority vocabularies produce too many fallback candidates")

validated_fallback: I | None = None
for candidate_values in product(*authority_options):
Expand All @@ -157,9 +140,7 @@ def __init__(self, specs: tuple[ActionSpec[I], ...], *, default: str, denied: st
"confidence": 0.0,
"brief": "",
}
candidate.update(
zip(authority_fields, candidate_values, strict=True)
)
candidate.update(zip(authority_fields, candidate_values, strict=True))
try:
validated_fallback = intent_type.model_validate(candidate)
except ValidationError:
Expand All @@ -173,12 +154,8 @@ def __init__(self, specs: tuple[ActionSpec[I], ...], *, default: str, denied: st
continue
break
if validated_fallback is None:
raise ConfigurationError(
"authority vocabularies cannot construct a valid fallback intent"
)
self._fallback_payload = MappingProxyType(
deepcopy(validated_fallback.model_dump(mode="python"))
)
raise ConfigurationError("authority vocabularies cannot construct a valid fallback intent")
self._fallback_payload = MappingProxyType(deepcopy(validated_fallback.model_dump(mode="python")))
self.default = default
self.denied = denied
self.intent_type = intent_type
Expand All @@ -195,14 +172,8 @@ def get(self, action: str) -> ActionSpec[I] | None:
return self._specs.get(action)

def _check_fallback_invariants(self, intent: I) -> None:
if (
intent.action != self.default
or not isfinite(intent.confidence)
or intent.confidence != 0.0
):
raise ConfigurationError(
"validated fallback intent changed its safety invariants"
)
if intent.action != self.default or not isfinite(intent.confidence) or intent.confidence != 0.0:
raise ConfigurationError("validated fallback intent changed its safety invariants")

def normalize_intent(self, value: object) -> tuple[I, bool]:
force_fallback = bool(getattr(value, _FALLBACK_MARKER, False))
Expand Down
18 changes: 4 additions & 14 deletions src/agent_kernel/integrations/agno/hitl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
class UnsupportedRequirement(ValueError):
def __init__(self, requirement_type: str):
self.requirement_type = requirement_type
super().__init__(
f"Agno requirement type is not supported: {requirement_type}"
)
super().__init__(f"Agno requirement type is not supported: {requirement_type}")


def _active_requirements(response: object) -> tuple[Any, ...]:
Expand Down Expand Up @@ -62,11 +60,7 @@ def translate_requirements(

for field in getattr(requirement, "user_input_schema", None) or ():
field_type = getattr(field, "field_type", None)
type_name = (
getattr(field_type, "__name__", str(field_type))
if field_type is not None
else None
)
type_name = getattr(field_type, "__name__", str(field_type)) if field_type is not None else None
translated.append(
{
"field": getattr(field, "name", None),
Expand All @@ -91,16 +85,12 @@ def apply_user_input(
for field in getattr(requirement, "user_input_schema", None) or ():
field_name = getattr(field, "name", None)
if not isinstance(field_name, str):
raise ValueError(
"Agno user-input requirement has an invalid field name"
)
raise ValueError("Agno user-input requirement has an invalid field name")
field_names.append(field_name)

missing = [name for name in field_names if name not in answers]
if missing:
raise ValueError(
f"missing answers for Agno user-input fields: {missing}"
)
raise ValueError(f"missing answers for Agno user-input fields: {missing}")
prepared.append(
(
requirement,
Expand Down
5 changes: 1 addition & 4 deletions src/agent_kernel/integrations/agno/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ def authority_hook(
) -> Any:
dependencies = getattr(run_context, "dependencies", None) or {}
envelope = dependencies.get("agent_kernel_authority")
if (
not isinstance(envelope, AuthorityEnvelope)
or function_name not in envelope.allowed
):
if not isinstance(envelope, AuthorityEnvelope) or function_name not in envelope.allowed:
raise PermissionError(f"principal may not call {function_name}")
return function_call(**args)
27 changes: 6 additions & 21 deletions src/agent_kernel/integrations/agno/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,7 @@ def execute(
raise ConfigurationError(f"missing Agno tool bindings: {missing}")

try:
selected_tools = [
self.tools[capability]
for capability in sorted(plan.capabilities)
]
selected_tools = [self.tools[capability] for capability in sorted(plan.capabilities)]
agent = self.agent_factory(
model=self.model,
tools=selected_tools,
Expand Down Expand Up @@ -137,23 +134,15 @@ def resume(
) -> RuntimeOutcome:
record_key = pause.adapter_state
if not isinstance(record_key, _PauseToken):
raise ConfigurationError(
"pause state was not created by this Agno runtime"
)
raise ConfigurationError("pause state was not created by this Agno runtime")
record = self._pause_records.get(record_key)
if record is None:
raise ConfigurationError(
"pause state was not created by this Agno runtime"
)
raise ConfigurationError("pause state was not created by this Agno runtime")
response = record.response
if pause.envelope != record.envelope:
raise ConfigurationError(
"pause authority envelope does not match the original run"
)
raise ConfigurationError("pause authority envelope does not match the original run")
if requirement_state(response) != record.requirement_state:
raise ConfigurationError(
"paused Agno response state does not match the original run"
)
raise ConfigurationError("paused Agno response state does not match the original run")

try:
apply_user_input(response, answers)
Expand All @@ -162,11 +151,7 @@ def resume(
dependencies={
"agent_kernel_authority": record.envelope,
},
user_id=(
record.context.user_id
if record.context is not None
else None
),
user_id=(record.context.user_id if record.context is not None else None),
)
if getattr(resumed, "is_paused", False):
outcome = self._pause(
Expand Down
13 changes: 3 additions & 10 deletions src/agent_kernel/integrations/dspy/artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ def _authority_vocabularies(
if continuation is None:
continue
vocabularies[spec.name] = {
field_name: sorted(values)
for field_name, values in sorted(
continuation.authority_fields.items()
)
field_name: sorted(values) for field_name, values in sorted(continuation.authority_fields.items())
}
return dict(sorted(vocabularies.items()))

Expand Down Expand Up @@ -95,17 +92,13 @@ def load_artifact(
expected_fingerprint = registry_fingerprint(registry)
artifact_fingerprint = metadata.get("registry_fingerprint")
if artifact_fingerprint != expected_fingerprint:
raise ConfigurationError(
"DSPy artifact registry fingerprint does not match the configured registry"
)
raise ConfigurationError("DSPy artifact registry fingerprint does not match the configured registry")
if metadata.get("format_version") != _FORMAT_VERSION:
raise ConfigurationError("unsupported DSPy artifact format version")

program_name = metadata.get("program_path")
if not isinstance(program_name, str) or program_name != path.name:
raise ConfigurationError(
"DSPy artifact metadata program path does not match the requested artifact"
)
raise ConfigurationError("DSPy artifact metadata program path does not match the requested artifact")

module.load(metadata_path.parent / program_name)
return module
Loading
Loading