22
33import types
44import typing
5- from typing import Any , get_args , get_origin
5+ from enum import Enum
6+ from typing import TYPE_CHECKING , Any , get_args , get_origin
67
78from pydantic import BaseModel
89
10+ if TYPE_CHECKING :
11+ from agentex .lib .sdk .state_machine .state import State
12+
913
1014class LifecycleState (BaseModel ):
1115 name : str
@@ -28,6 +32,57 @@ class AgentCard(BaseModel):
2832 input_types : list [str ] = []
2933 output_schema : dict | None = None
3034
35+ @classmethod
36+ def from_states (
37+ cls ,
38+ initial_state : str | Enum ,
39+ states : list [State ],
40+ output_event_model : type [BaseModel ] | None = None ,
41+ extra_input_types : list [str ] | None = None ,
42+ queries : list [str ] | None = None ,
43+ ) -> AgentCard :
44+ """Build an AgentCard directly from a list[State] + initial_state.
45+
46+ Agents can share their `states` list between the StateMachine and acp.py
47+ without constructing a temporary StateMachine instance.
48+ """
49+ lifecycle_states = [
50+ LifecycleState (
51+ name = state .name ,
52+ description = state .workflow .description ,
53+ waits_for_input = state .workflow .waits_for_input ,
54+ accepts = list (state .workflow .accepts ),
55+ transitions = [
56+ t .value if isinstance (t , Enum ) else str (t )
57+ for t in state .workflow .transitions
58+ ],
59+ )
60+ for state in states
61+ ]
62+
63+ initial = initial_state .value if isinstance (initial_state , Enum ) else initial_state
64+
65+ data_events : list [str ] = []
66+ output_schema : dict | None = None
67+ if output_event_model :
68+ data_events = extract_literal_values (output_event_model , "type" )
69+ output_schema = output_event_model .model_json_schema ()
70+
71+ derived_input_types : set [str ] = set ()
72+ for ls in lifecycle_states :
73+ derived_input_types .update (ls .accepts )
74+
75+ return cls (
76+ lifecycle = AgentLifecycle (
77+ states = lifecycle_states ,
78+ initial_state = initial ,
79+ queries = queries or [],
80+ ),
81+ data_events = data_events ,
82+ input_types = sorted (derived_input_types | set (extra_input_types or [])),
83+ output_schema = output_schema ,
84+ )
85+
3186 @classmethod
3287 def from_state_machine (
3388 cls ,
@@ -36,21 +91,37 @@ def from_state_machine(
3691 extra_input_types : list [str ] | None = None ,
3792 queries : list [str ] | None = None ,
3893 ) -> AgentCard :
39- lifecycle_data = state_machine .get_lifecycle ()
40- lifecycle_data ["queries" ] = queries or []
94+ """Build an AgentCard from a StateMachine instance. Delegates to from_states()."""
95+ lifecycle = state_machine .get_lifecycle ()
96+ states_data = lifecycle ["states" ]
97+ initial = lifecycle ["initial_state" ]
4198
99+ # Reconstruct lightweight State-like objects from the lifecycle dict
100+ # so we can reuse from_states logic via the dict path
42101 data_events : list [str ] = []
43102 output_schema : dict | None = None
44103 if output_event_model :
45104 data_events = extract_literal_values (output_event_model , "type" )
46105 output_schema = output_event_model .model_json_schema ()
47106
48107 derived_input_types : set [str ] = set ()
49- for state in lifecycle_data ["states" ]:
50- derived_input_types .update (state .get ("accepts" , []))
108+ lifecycle_states = []
109+ for s in states_data :
110+ derived_input_types .update (s .get ("accepts" , []))
111+ lifecycle_states .append (LifecycleState (
112+ name = s ["name" ],
113+ description = s .get ("description" , "" ),
114+ waits_for_input = s .get ("waits_for_input" , False ),
115+ accepts = s .get ("accepts" , []),
116+ transitions = s .get ("transitions" , []),
117+ ))
51118
52119 return cls (
53- lifecycle = AgentLifecycle .model_validate (lifecycle_data ),
120+ lifecycle = AgentLifecycle (
121+ states = lifecycle_states ,
122+ initial_state = initial ,
123+ queries = queries or [],
124+ ),
54125 data_events = data_events ,
55126 input_types = sorted (derived_input_types | set (extra_input_types or [])),
56127 output_schema = output_schema ,
0 commit comments