-
Notifications
You must be signed in to change notification settings - Fork 1
feat: implement meeting protocol system (#123) #167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
d1755f6
af5d72a
3101925
2aa2a1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -578,7 +578,7 @@ When a loop is detected, the framework: | |||||
| 3. Escalates to the sender's manager (or human if at top of hierarchy) | ||||||
| 4. Logs the loop for analytics and process improvement | ||||||
|
|
||||||
| > **Current state (M4 in-progress):** The communication foundation is implemented: `MessageBus` protocol with `InMemoryMessageBus` backend (asyncio queues, pull-model `receive()`), `MessageDispatcher` for concurrent handler routing via `asyncio.TaskGroup`, `AgentMessenger` per-agent facade (auto-fills sender/timestamp/ID, deterministic direct-channel naming `@{sorted_a}:{sorted_b}`), and `DeliveryEnvelope` for delivery tracking. Loop prevention (§5.5) is implemented: `DelegationGuard` orchestrates five mechanisms (ancestry, depth, dedup, rate limit, circuit breaker) with `LoopPreventionConfig`. Hierarchical delegation is implemented via `DelegationService` with `HierarchyResolver` and `AuthorityValidator`. Task model extended with `parent_task_id` and `delegation_chain` fields. Conflict resolution (§5.6) and meeting protocol (§5.7) are planned for later M4 work. | ||||||
| > **Current state (M4 in-progress):** The communication foundation is implemented: `MessageBus` protocol with `InMemoryMessageBus` backend (asyncio queues, pull-model `receive()`), `MessageDispatcher` for concurrent handler routing via `asyncio.TaskGroup`, `AgentMessenger` per-agent facade (auto-fills sender/timestamp/ID, deterministic direct-channel naming `@{sorted_a}:{sorted_b}`), and `DeliveryEnvelope` for delivery tracking. Loop prevention (§5.5) is implemented: `DelegationGuard` orchestrates five mechanisms (ancestry, depth, dedup, rate limit, circuit breaker) with `LoopPreventionConfig`. Hierarchical delegation is implemented via `DelegationService` with `HierarchyResolver` and `AuthorityValidator`. Task model extended with `parent_task_id` and `delegation_chain` fields. Meeting protocol (§5.7) is implemented with all 3 protocols (round-robin, position papers, structured phases) via `MeetingOrchestrator` in `communication/meeting/`. Conflict resolution (§5.6) is planned for later M4 work. | ||||||
|
|
||||||
| ### 5.6 Conflict Resolution Protocol | ||||||
|
|
||||||
|
|
@@ -650,7 +650,7 @@ conflict_resolution: | |||||
|
|
||||||
| Meetings (§5.1 Pattern 3) follow configurable protocols that determine how agents interact during structured multi-agent conversations. Different meeting types naturally suit different protocols. All protocols implement a `MeetingProtocol` protocol, making the system extensible — new protocols can be registered and selected per meeting type. Cost bounds are enforced by `duration_tokens` in meeting config (§5.4). | ||||||
|
|
||||||
| > **MVP: Not in M3.** Meetings are an M4 feature. Round-Robin (Protocol 1) is the initial default. | ||||||
| > **Current state (M4 complete):** All 3 meeting protocols are implemented in `communication/meeting/`: `RoundRobinProtocol`, `PositionPapersProtocol`, and `StructuredPhasesProtocol`. The `MeetingOrchestrator` runs meetings end-to-end with token budget enforcement via `TokenBudgetTracker`. All protocols implement the `MeetingProtocol` protocol interface. | ||||||
|
greptile-apps[bot] marked this conversation as resolved.
Outdated
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update the spec to the current meeting symbol names. These sections still refer to Also applies to: 2421-2425 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| #### Protocol 1: Round-Robin Transcript | ||||||
|
|
||||||
|
|
@@ -694,10 +694,10 @@ Meeting split into phases with targeted participation: | |||||
|
|
||||||
| ```yaml | ||||||
| meeting_protocol: "structured_phases" | ||||||
| auto_create_tasks: true # action items become tasks (top-level, applies to any protocol) | ||||||
| structured_phases: | ||||||
| skip_discussion_if_no_conflicts: true | ||||||
| max_discussion_tokens: 1000 | ||||||
| auto_create_tasks: true # action items become tasks | ||||||
| ``` | ||||||
|
|
||||||
| - Cost-efficient — parallel input, discussion only when needed | ||||||
|
|
@@ -2415,6 +2415,19 @@ ai-company/ | |||||
| │ │ │ ├── models.py # GuardCheckOutcome | ||||||
| │ │ │ └── rate_limit.py # DelegationRateLimiter (per-pair) | ||||||
| │ │ ├── message.py # Message model | ||||||
| │ │ ├── meeting/ # Meeting protocol subsystem | ||||||
| │ │ │ ├── __init__.py # Package exports | ||||||
| │ │ │ ├── _prompts.py # LLM prompt templates for meeting phases | ||||||
| │ │ │ ├── _token_tracker.py # TokenBudgetTracker for duration_tokens enforcement | ||||||
|
||||||
| │ │ │ ├── _token_tracker.py # TokenBudgetTracker for duration_tokens enforcement | |
| │ │ │ ├── _token_tracker.py # TokenTracker for duration_tokens enforcement |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| CommunicationPattern, | ||
| MessageBusBackend, | ||
| ) | ||
| from ai_company.communication.meeting.config import MeetingProtocolConfig | ||
| from ai_company.core.types import ( | ||
| NotBlankStr, | ||
| validate_unique_strings, | ||
|
|
@@ -109,6 +110,10 @@ class MeetingTypeConfig(BaseModel): | |
| gt=0, | ||
| description="Token budget for the meeting", | ||
| ) | ||
| protocol_config: MeetingProtocolConfig = Field( | ||
| default_factory=MeetingProtocolConfig, | ||
| description="Meeting protocol configuration", | ||
| ) | ||
|
Comment on lines
+116
to
+119
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update
🤖 Prompt for AI Agents |
||
|
|
||
| @model_validator(mode="after") | ||
| def _validate_frequency_or_trigger(self) -> Self: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| """Meeting protocol subsystem (DESIGN_SPEC Section 5.7). | ||
|
|
||
| Provides pluggable meeting protocol strategies for structured | ||
| multi-agent conversations: | ||
|
|
||
| - **Round-Robin**: Sequential turns with full transcript context. | ||
| - **Position Papers**: Parallel independent papers, then synthesis. | ||
| - **Structured Phases**: Phased agenda with conditional discussion. | ||
| """ | ||
|
|
||
| from ai_company.communication.meeting.config import ( | ||
| MeetingProtocolConfig, | ||
| PositionPapersConfig, | ||
| RoundRobinConfig, | ||
| StructuredPhasesConfig, | ||
| ) | ||
| from ai_company.communication.meeting.enums import ( | ||
| MeetingPhase, | ||
| MeetingProtocolType, | ||
| MeetingStatus, | ||
| ) | ||
| from ai_company.communication.meeting.errors import ( | ||
| MeetingAgentError, | ||
| MeetingBudgetExhaustedError, | ||
| MeetingError, | ||
| MeetingParticipantError, | ||
| MeetingProtocolNotFoundError, | ||
| ) | ||
| from ai_company.communication.meeting.models import ( | ||
| ActionItem, | ||
| AgentResponse, | ||
| MeetingAgenda, | ||
| MeetingAgendaItem, | ||
| MeetingContribution, | ||
| MeetingMinutes, | ||
| MeetingRecord, | ||
| ) | ||
| from ai_company.communication.meeting.orchestrator import MeetingOrchestrator | ||
| from ai_company.communication.meeting.position_papers import ( | ||
| PositionPapersProtocol, | ||
| ) | ||
| from ai_company.communication.meeting.protocol import ( | ||
| AgentCaller, | ||
| MeetingProtocol, | ||
| TaskCreator, | ||
| ) | ||
| from ai_company.communication.meeting.round_robin import RoundRobinProtocol | ||
| from ai_company.communication.meeting.structured_phases import ( | ||
| StructuredPhasesProtocol, | ||
| ) | ||
|
|
||
| __all__ = [ | ||
| "ActionItem", | ||
| "AgentCaller", | ||
| "AgentResponse", | ||
| "MeetingAgenda", | ||
| "MeetingAgendaItem", | ||
| "MeetingAgentError", | ||
| "MeetingBudgetExhaustedError", | ||
| "MeetingContribution", | ||
| "MeetingError", | ||
| "MeetingMinutes", | ||
| "MeetingOrchestrator", | ||
| "MeetingParticipantError", | ||
| "MeetingPhase", | ||
| "MeetingProtocol", | ||
| "MeetingProtocolConfig", | ||
| "MeetingProtocolNotFoundError", | ||
| "MeetingProtocolType", | ||
| "MeetingRecord", | ||
| "MeetingStatus", | ||
| "PositionPapersConfig", | ||
| "PositionPapersProtocol", | ||
| "RoundRobinConfig", | ||
| "RoundRobinProtocol", | ||
| "StructuredPhasesConfig", | ||
| "StructuredPhasesProtocol", | ||
| "TaskCreator", | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,28 @@ | ||||||||||||||||||||||||
| """Shared prompt builders for meeting protocol implementations.""" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| from typing import TYPE_CHECKING | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| if TYPE_CHECKING: | ||||||||||||||||||||||||
| from ai_company.communication.meeting.models import MeetingAgenda | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def build_agenda_prompt(agenda: MeetingAgenda) -> str: | ||||||||||||||||||||||||
|
Comment on lines
+3
to
+9
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: With PEP 649 lazy annotations (Python 3.14+), the annotation expression isn’t evaluated at function definition time, so referencing a name that’s only imported under This matches the general PEP 649/749 model: evaluation is deferred, but when the deferred evaluation runs, missing names can still produce runtime errors like Implication: if you want Sources: 🏁 Script executed: # Check if there's a circular import between models.py and _prompts.py
cd src/ai_company/communication/meeting
echo "=== Checking models.py imports ==="
head -20 models.py | grep -E "^(import|from)" || echo "No imports in first 20 lines"
echo -e "\n=== Checking _prompts.py imports ==="
head -20 _prompts.py | grep -E "^(import|from)" || echo "No imports in first 20 lines"
echo -e "\n=== Checking if models.py imports from _prompts.py ==="
grep -n "_prompts" models.py || echo "No imports from _prompts in models.py"Repository: Aureliolo/ai-company Length of output: 477 🏁 Script executed: # Read full _prompts.py to see the complete function implementation
cat -n src/ai_company/communication/meeting/_prompts.pyRepository: Aureliolo/ai-company Length of output: 1097 🏁 Script executed: # Check MeetingAgenda and MeetingAgendaItem definitions in models.py
cat -n src/ai_company/communication/meeting/models.py | head -100Repository: Aureliolo/ai-company Length of output: 3382 🏁 Script executed: # Find usages of build_agenda_prompt
rg "build_agenda_prompt" --type py -B 2 -A 2Repository: Aureliolo/ai-company Length of output: 3462 🏁 Script executed: # Check for test files
find src/ai_company/communication/meeting -name "*test*" -o -name "test_*"Repository: Aureliolo/ai-company Length of output: 46 🏁 Script executed: # Check if there's documentation about what presenter_id should do
grep -rn "presenter_id" src/ai_company/communication/meeting/ --type pyRepository: Aureliolo/ai-company Length of output: 181 🏁 Script executed: # Search for presenter_id usage in meeting module
rg "presenter_id" src/ai_company/communication/meeting/Repository: Aureliolo/ai-company Length of output: 266 🏁 Script executed: # Check if there are any test files for the prompts
find . -path "*/test*" -name "*prompt*" -o -path "*/test*" -name "*meeting*" | grep -E "(test|spec)" | head -20Repository: Aureliolo/ai-company Length of output: 239 🏁 Script executed: # Check the docstring and usage context more broadly
rg "presenter" src/ai_company/communication/meeting/ -B 2 -A 2Repository: Aureliolo/ai-company Length of output: 888 🏁 Script executed: # Check unit tests for meeting module to see what's expected
ls -la tests/unit/communication/meeting/Repository: Aureliolo/ai-company Length of output: 918 🏁 Script executed: # Look for tests specifically for _prompts.py
cat tests/unit/communication/meeting/*prompt* 2>/dev/null || echo "No prompt tests found, checking all tests"
ls tests/unit/communication/meeting/Repository: Aureliolo/ai-company Length of output: 296 🏁 Script executed: # Check integration test for meeting to see how presenter_id is used
cat tests/integration/communication/test_meeting_integration.py | head -100Repository: Aureliolo/ai-company Length of output: 2998 Use a real import for
Safer import pattern-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
- from ai_company.communication.meeting.models import MeetingAgenda
+from ai_company.communication.meeting.models import MeetingAgenda📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| """Build the initial agenda prompt text. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Args: | ||||||||||||||||||||||||
| agenda: The meeting agenda to format. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Returns: | ||||||||||||||||||||||||
| Formatted agenda text for use in agent prompts. | ||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
| parts = [f"Meeting: {agenda.title}"] | ||||||||||||||||||||||||
| if agenda.context: | ||||||||||||||||||||||||
| parts.append(f"Context: {agenda.context}") | ||||||||||||||||||||||||
| if agenda.items: | ||||||||||||||||||||||||
| parts.append("Agenda items:") | ||||||||||||||||||||||||
| for i, item in enumerate(agenda.items, 1): | ||||||||||||||||||||||||
| entry = f" {i}. {item.title}" | ||||||||||||||||||||||||
| if item.description: | ||||||||||||||||||||||||
| entry += f" — {item.description}" | ||||||||||||||||||||||||
| parts.append(entry) | ||||||||||||||||||||||||
|
Comment on lines
+23
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't drop
📝 Suggested formatter fix for i, item in enumerate(agenda.items, 1):
entry = f" {i}. {item.title}"
+ if item.presenter_id:
+ entry += f" (presenter: {item.presenter_id})"
if item.description:
entry += f" — {item.description}"
parts.append(entry)🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| return "\n".join(parts) | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| """Shared token budget tracker for meeting protocol implementations.""" | ||
|
|
||
| import dataclasses | ||
|
|
||
|
|
||
| @dataclasses.dataclass | ||
| class TokenTracker: | ||
| """Mutable token budget tracker scoped to a single meeting execution. | ||
|
|
||
| Attributes: | ||
| budget: Total token budget for the meeting. | ||
| input_tokens: Total prompt tokens consumed so far. | ||
| output_tokens: Total response tokens generated so far. | ||
| """ | ||
|
|
||
| budget: int | ||
| input_tokens: int = 0 | ||
| output_tokens: int = 0 | ||
|
|
||
| @property | ||
| def used(self) -> int: | ||
| """Total tokens consumed so far.""" | ||
| return self.input_tokens + self.output_tokens | ||
|
|
||
| @property | ||
| def remaining(self) -> int: | ||
| """Tokens remaining in the budget.""" | ||
| return max(0, self.budget - self.used) | ||
|
|
||
| @property | ||
| def is_exhausted(self) -> bool: | ||
| """Whether the budget is fully consumed.""" | ||
| return self.remaining == 0 | ||
|
|
||
| def record(self, input_tokens: int, output_tokens: int) -> None: | ||
| """Record token usage from an agent call.""" | ||
| self.input_tokens += input_tokens | ||
| self.output_tokens += output_tokens | ||
|
Comment on lines
+16
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Prefer copy-on-write state over mutating This helper introduces shared mutable state via a dataclass and As per coding guidelines, "Create new objects instead of mutating existing ones" and "Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (model_copy(update=...)) for runtime state that evolves; never mix static config fields with mutable runtime fields". 🤖 Prompt for AI Agents
Comment on lines
+41
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fail fast when a token record would go negative or exceed the budget.
🛡️ Proposed fix+from ai_company.communication.meeting.errors import MeetingBudgetExhaustedError
+
@@
def record(self, input_tokens: int, output_tokens: int) -> None:
"""Record token usage from an agent call."""
+ if input_tokens < 0 or output_tokens < 0:
+ msg = "token counts must be non-negative"
+ raise ValueError(msg)
+ projected_total = self.used + input_tokens + output_tokens
+ if projected_total > self.budget:
+ msg = (
+ "meeting token budget exhausted: "
+ f"projected_total={projected_total}, budget={self.budget}"
+ )
+ raise MeetingBudgetExhaustedError(msg)
self.input_tokens += input_tokens
self.output_tokens += output_tokens🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section says budget enforcement is done via
TokenBudgetTracker, but the implementation added incommunication/meeting/_token_tracker.pydefinesTokenTracker. Update the name here to match the actual type to keep the spec accurate/searchable.