ag2-tealtiger 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ag2_tealtiger-0.1.0/.gitignore +167 -0
- ag2_tealtiger-0.1.0/LICENSE +17 -0
- ag2_tealtiger-0.1.0/PKG-INFO +109 -0
- ag2_tealtiger-0.1.0/README.md +74 -0
- ag2_tealtiger-0.1.0/examples/governed_groupchat.py +225 -0
- ag2_tealtiger-0.1.0/examples/policy_enforcement.py +234 -0
- ag2_tealtiger-0.1.0/examples/zero_config_observe.py +209 -0
- ag2_tealtiger-0.1.0/pyproject.toml +99 -0
- ag2_tealtiger-0.1.0/tests/__init__.py +0 -0
- ag2_tealtiger-0.1.0/tests/conftest.py +431 -0
- ag2_tealtiger-0.1.0/tests/strategies.py +294 -0
- ag2_tealtiger-0.1.0/tests/test_audit_agent.py +411 -0
- ag2_tealtiger-0.1.0/tests/test_audit_export.py +405 -0
- ag2_tealtiger-0.1.0/tests/test_budget_manager.py +349 -0
- ag2_tealtiger-0.1.0/tests/test_decision_manager.py +385 -0
- ag2_tealtiger-0.1.0/tests/test_error_handling.py +618 -0
- ag2_tealtiger-0.1.0/tests/test_exceptions.py +236 -0
- ag2_tealtiger-0.1.0/tests/test_governed_groupchat.py +553 -0
- ag2_tealtiger-0.1.0/tests/test_guard.py +362 -0
- ag2_tealtiger-0.1.0/tests/test_guard_budget.py +490 -0
- ag2_tealtiger-0.1.0/tests/test_guard_freeze.py +483 -0
- ag2_tealtiger-0.1.0/tests/test_guard_message_governance.py +555 -0
- ag2_tealtiger-0.1.0/tests/test_guard_observe_mode.py +904 -0
- ag2_tealtiger-0.1.0/tests/test_guard_policy_mode.py +550 -0
- ag2_tealtiger-0.1.0/tests/test_idempotency.py +107 -0
- ag2_tealtiger-0.1.0/tests/test_integration.py +833 -0
- ag2_tealtiger-0.1.0/tests/test_package_exports.py +131 -0
- ag2_tealtiger-0.1.0/tests/test_pii.py +311 -0
- ag2_tealtiger-0.1.0/tests/test_property_12_fail_closed.py +247 -0
- ag2_tealtiger-0.1.0/tests/test_property_13_fail_open.py +273 -0
- ag2_tealtiger-0.1.0/tests/test_property_14_denial_parseability.py +196 -0
- ag2_tealtiger-0.1.0/tests/test_property_17_budget_enforcement.py +175 -0
- ag2_tealtiger-0.1.0/tests/test_property_18_budget_warning.py +289 -0
- ag2_tealtiger-0.1.0/tests/test_property_19_budget_arithmetic.py +141 -0
- ag2_tealtiger-0.1.0/tests/test_property_1_audit_completeness.py +407 -0
- ag2_tealtiger-0.1.0/tests/test_property_20_cost_monotonicity.py +156 -0
- ag2_tealtiger-0.1.0/tests/test_property_24_decision_expiry.py +215 -0
- ag2_tealtiger-0.1.0/tests/test_property_25_revalidation.py +362 -0
- ag2_tealtiger-0.1.0/tests/test_property_2_observe_passthrough.py +286 -0
- ag2_tealtiger-0.1.0/tests/test_property_3_frozen_denial.py +238 -0
- ag2_tealtiger-0.1.0/tests/test_property_4_freeze_roundtrip.py +287 -0
- ag2_tealtiger-0.1.0/tests/test_property_5_agent_isolation.py +256 -0
- ag2_tealtiger-0.1.0/tests/test_property_7_decision_uniqueness.py +228 -0
- ag2_tealtiger-0.1.0/tests/test_property_8_idempotency_key_determinism.py +65 -0
- ag2_tealtiger-0.1.0/tests/test_property_9_retry_idempotency.py +307 -0
- ag2_tealtiger-0.1.0/tests/test_property_conversation_id_stability.py +190 -0
- ag2_tealtiger-0.1.0/tests/test_property_groupchat.py +730 -0
- ag2_tealtiger-0.1.0/tests/test_property_nested_chat_correlation.py +302 -0
- ag2_tealtiger-0.1.0/tests/test_property_params_hash.py +108 -0
- ag2_tealtiger-0.1.0/tests/test_resolve_refer.py +544 -0
- ag2_tealtiger-0.1.0/tests/test_teec_builder.py +247 -0
- ag2_tealtiger-0.1.0/tests/test_types.py +437 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
# package-lock.json committed for Scorecard Pinned-Dependencies compliance
|
|
4
|
+
|
|
5
|
+
# Build output
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
storybook-static/
|
|
9
|
+
*.tsbuildinfo
|
|
10
|
+
|
|
11
|
+
# Test coverage
|
|
12
|
+
coverage/
|
|
13
|
+
.nyc_output/
|
|
14
|
+
htmlcov/
|
|
15
|
+
.coverage
|
|
16
|
+
.hypothesis/
|
|
17
|
+
|
|
18
|
+
# Environment variables
|
|
19
|
+
.env
|
|
20
|
+
.env.local
|
|
21
|
+
.env.*.local
|
|
22
|
+
|
|
23
|
+
# IDE
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
*.swp
|
|
27
|
+
*.swo
|
|
28
|
+
*~
|
|
29
|
+
|
|
30
|
+
# Logs
|
|
31
|
+
logs/
|
|
32
|
+
*.log
|
|
33
|
+
npm-debug.log*
|
|
34
|
+
yarn-debug.log*
|
|
35
|
+
yarn-error.log*
|
|
36
|
+
lerna-debug.log*
|
|
37
|
+
|
|
38
|
+
# Temporary files
|
|
39
|
+
tmp/
|
|
40
|
+
temp/
|
|
41
|
+
*.tmp
|
|
42
|
+
|
|
43
|
+
# OS files
|
|
44
|
+
Thumbs.db
|
|
45
|
+
.DS_Store
|
|
46
|
+
|
|
47
|
+
# ── SENSITIVE / INTERNAL — DO NOT COMMIT ─────────────────────────
|
|
48
|
+
|
|
49
|
+
# Internal documentation and planning
|
|
50
|
+
agentguard-internal-docs/
|
|
51
|
+
.kiro/
|
|
52
|
+
|
|
53
|
+
# Internal analysis and strategy docs (root-level)
|
|
54
|
+
*-INTERNAL-*.md
|
|
55
|
+
*-STRATEGY.md
|
|
56
|
+
*-ANALYSIS.md
|
|
57
|
+
*-FEASIBILITY.md
|
|
58
|
+
*-ALIGNMENT.md
|
|
59
|
+
BRANDING-*.md
|
|
60
|
+
CONTAINERIZATION-*.md
|
|
61
|
+
DEPLOYMENT-*.md
|
|
62
|
+
KUBERNETES-*.md
|
|
63
|
+
PHASE-*.md
|
|
64
|
+
POLICY-REGISTRY-*.md
|
|
65
|
+
POST-LAUNCH-*.md
|
|
66
|
+
TYPESCRIPT-PYTHON-PARITY-*.md
|
|
67
|
+
TEALTIGER-*-ANALYSIS.md
|
|
68
|
+
TEALTIGER-*-ALIGNMENT.md
|
|
69
|
+
TEALTIGER-VS-*.md
|
|
70
|
+
TEALTIGER-PLAYGROUND-*.md
|
|
71
|
+
TEALTIGER-SERVERLESS-*.md
|
|
72
|
+
TEALTIGER-SDK-CONTAINERIZATION-*.md
|
|
73
|
+
TEALTIGER-CLINE-*.md
|
|
74
|
+
DOCKER_README.md
|
|
75
|
+
DOCKER-TEST-RESULTS.md
|
|
76
|
+
|
|
77
|
+
# Internal scripts
|
|
78
|
+
scripts/*
|
|
79
|
+
!scripts/validate-policy.ts
|
|
80
|
+
!scripts/test-validate-policy.ts
|
|
81
|
+
push-examples.sh
|
|
82
|
+
push-examples.bat
|
|
83
|
+
cleanup-*.bat
|
|
84
|
+
copy-*.bat
|
|
85
|
+
final-*.bat
|
|
86
|
+
remove-*.bat
|
|
87
|
+
update-*.bat
|
|
88
|
+
CLEANUP-ACTION-REQUIRED.txt
|
|
89
|
+
|
|
90
|
+
# Legacy / deprecated directories
|
|
91
|
+
agentguard/
|
|
92
|
+
agentguard-landing/
|
|
93
|
+
agent-guard-sdk-public/
|
|
94
|
+
test-deprecated/
|
|
95
|
+
test-install/
|
|
96
|
+
tealtiger/
|
|
97
|
+
|
|
98
|
+
# Infrastructure (should be in separate repos)
|
|
99
|
+
ansible/
|
|
100
|
+
helm/
|
|
101
|
+
helm-charts-repo/
|
|
102
|
+
terraform-aws-tealtiger-repo/
|
|
103
|
+
docker/
|
|
104
|
+
database/
|
|
105
|
+
Dockerfile
|
|
106
|
+
docker-compose*.yml
|
|
107
|
+
|
|
108
|
+
# Internal source (monorepo backend — not for public SDK repo)
|
|
109
|
+
src/
|
|
110
|
+
!dashboard/governance-feed/src/
|
|
111
|
+
!dashboard/governance-feed/src/**
|
|
112
|
+
|
|
113
|
+
# Postman collections
|
|
114
|
+
postman/
|
|
115
|
+
|
|
116
|
+
# Blog drafts and diagrams (published separately)
|
|
117
|
+
blog/
|
|
118
|
+
|
|
119
|
+
# Internal governance / SOT (separate repo)
|
|
120
|
+
TealTiger-SOT/
|
|
121
|
+
tealtiger-sot-repo/
|
|
122
|
+
|
|
123
|
+
# Landing pages and websites (separate repos)
|
|
124
|
+
TealTiger-LandingPage/
|
|
125
|
+
TealTiger-Blog/
|
|
126
|
+
TealTiger-Cookbook/
|
|
127
|
+
TealTiger-MCP/
|
|
128
|
+
tealtiger-docs-repo/
|
|
129
|
+
tealtiger-docs-source/
|
|
130
|
+
tealtiger-docs-target/
|
|
131
|
+
tealtiger-action-repo/
|
|
132
|
+
|
|
133
|
+
# Staging directories (development only)
|
|
134
|
+
staging-py/
|
|
135
|
+
staging-ts/
|
|
136
|
+
|
|
137
|
+
# Package build artifacts
|
|
138
|
+
package/
|
|
139
|
+
|
|
140
|
+
# HTML coverage reports
|
|
141
|
+
htmlcov/
|
|
142
|
+
|
|
143
|
+
# Vertex extension (internal)
|
|
144
|
+
packages/tealtiger-vertex-extension/
|
|
145
|
+
|
|
146
|
+
# GitHub internal guides
|
|
147
|
+
.github/ENABLE-*.md
|
|
148
|
+
.github/SAMPLE_ISSUES.md
|
|
149
|
+
.github/SOCIAL-PREVIEW-SPEC.md
|
|
150
|
+
|
|
151
|
+
# Internal workflow files
|
|
152
|
+
.github/workflows/docker-variants.yml
|
|
153
|
+
.github/workflows/publish-circleci-orb.yml
|
|
154
|
+
.github/workflows/v1-1-0-launch.yml
|
|
155
|
+
|
|
156
|
+
# Build configs (monorepo-level, not needed by SDK users)
|
|
157
|
+
jest.config.js
|
|
158
|
+
.eslintrc.js
|
|
159
|
+
|
|
160
|
+
# Dashboard package source and lockfile
|
|
161
|
+
!packages/tealtiger-dashboard/src/
|
|
162
|
+
!packages/tealtiger-dashboard/src/**
|
|
163
|
+
!packages/tealtiger-dashboard/package-lock.json
|
|
164
|
+
|
|
165
|
+
# Misc internal files
|
|
166
|
+
.cleanup-sensitive-files.txt
|
|
167
|
+
*.bat
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 TealTiger Team
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ag2-tealtiger
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Deterministic governance adapter for AG2 (AutoGen fork) — policy enforcement, PII detection, cost tracking, per-agent kill switch, governed speaker selection, and structured audit evidence. No LLM in the governance path.
|
|
5
|
+
Project-URL: Homepage, https://tealtiger.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.tealtiger.ai/integrations/ag2
|
|
7
|
+
Project-URL: Repository, https://github.com/agentguard-ai/tealtiger
|
|
8
|
+
Project-URL: Issues, https://github.com/agentguard-ai/tealtiger/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/agentguard-ai/tealtiger/blob/main/packages/ag2-tealtiger/CHANGELOG.md
|
|
10
|
+
Author-email: TealTiger Team <reachout@tealtiger.ai>
|
|
11
|
+
License: Apache-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: ag2,ai-agents,audit,autogen,cost-tracking,deterministic,governance,groupchat,guardrails,kill-switch,llm,multi-agent,pii,policy,security,tealtiger
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: ag2>=0.4.0
|
|
27
|
+
Requires-Dist: tealtiger>=1.3.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: hypothesis>=6.0.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.5.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# ag2-tealtiger
|
|
37
|
+
|
|
38
|
+
Deterministic governance adapter for [AG2](https://github.com/ag2ai/ag2) (AutoGen fork) — policy enforcement, PII detection, cost tracking, per-agent kill switch, governed speaker selection, and structured audit evidence.
|
|
39
|
+
|
|
40
|
+
**No LLM in the governance path.** All policy evaluation is deterministic, adding <5ms latency.
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/ag2-tealtiger/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
[](https://pypi.org/project/ag2-tealtiger/)
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install ag2-tealtiger
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
### Zero-Config Observe Mode
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from ag2_tealtiger import TealTigerGuard
|
|
58
|
+
|
|
59
|
+
guard = TealTigerGuard() # No engine = observe mode
|
|
60
|
+
guard.attach(my_agent)
|
|
61
|
+
# Cost, PII, and tool usage tracked automatically. Nothing blocked.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or the convenience subclass:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from ag2_tealtiger import TealTigerAuditAgent
|
|
68
|
+
|
|
69
|
+
agent = TealTigerAuditAgent(name="coder") # Zero config, observe mode built-in
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Policy Enforcement
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from ag2_tealtiger import TealTigerGuard, GovernanceMode
|
|
76
|
+
|
|
77
|
+
guard = TealTigerGuard(engine=my_engine, mode=GovernanceMode.ENFORCE)
|
|
78
|
+
guard.attach(agent)
|
|
79
|
+
# DENY blocks the call with a structured denial message
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Governed GroupChat
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from ag2_tealtiger import GovernedGroupChat, TealTigerGuard, GovernanceMode
|
|
86
|
+
|
|
87
|
+
guard = TealTigerGuard(engine=engine, mode=GovernanceMode.ENFORCE)
|
|
88
|
+
group_chat = GovernedGroupChat(agents=[agent1, agent2, agent3], guard=guard)
|
|
89
|
+
speaker = group_chat.select_speaker(last_speaker=agent1)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Features
|
|
93
|
+
|
|
94
|
+
| Feature | Observe | Monitor | Enforce |
|
|
95
|
+
|---------|:-------:|:-------:|:-------:|
|
|
96
|
+
| Cost tracking per agent | ✅ | ✅ | ✅ |
|
|
97
|
+
| PII detection in tool args | ✅ | ✅ | ✅ |
|
|
98
|
+
| Structured audit entries (TEEC) | ✅ | ✅ | ✅ |
|
|
99
|
+
| Per-agent freeze/unfreeze | ✅ | ✅ | ✅ |
|
|
100
|
+
| Policy evaluation | — | ✅ (log) | ✅ (block) |
|
|
101
|
+
| Budget enforcement | — | ✅ (log) | ✅ (block) |
|
|
102
|
+
| REFER escalation | — | ✅ | ✅ |
|
|
103
|
+
| Inter-agent message governance | ✅ | ✅ | ✅ |
|
|
104
|
+
| Governed speaker selection | ✅ | ✅ | ✅ |
|
|
105
|
+
| JSONL audit export | ✅ | ✅ | ✅ |
|
|
106
|
+
|
|
107
|
+
## License
|
|
108
|
+
|
|
109
|
+
Apache-2.0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ag2-tealtiger
|
|
2
|
+
|
|
3
|
+
Deterministic governance adapter for [AG2](https://github.com/ag2ai/ag2) (AutoGen fork) — policy enforcement, PII detection, cost tracking, per-agent kill switch, governed speaker selection, and structured audit evidence.
|
|
4
|
+
|
|
5
|
+
**No LLM in the governance path.** All policy evaluation is deterministic, adding <5ms latency.
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/ag2-tealtiger/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://pypi.org/project/ag2-tealtiger/)
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install ag2-tealtiger
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### Zero-Config Observe Mode
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from ag2_tealtiger import TealTigerGuard
|
|
23
|
+
|
|
24
|
+
guard = TealTigerGuard() # No engine = observe mode
|
|
25
|
+
guard.attach(my_agent)
|
|
26
|
+
# Cost, PII, and tool usage tracked automatically. Nothing blocked.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or the convenience subclass:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from ag2_tealtiger import TealTigerAuditAgent
|
|
33
|
+
|
|
34
|
+
agent = TealTigerAuditAgent(name="coder") # Zero config, observe mode built-in
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Policy Enforcement
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from ag2_tealtiger import TealTigerGuard, GovernanceMode
|
|
41
|
+
|
|
42
|
+
guard = TealTigerGuard(engine=my_engine, mode=GovernanceMode.ENFORCE)
|
|
43
|
+
guard.attach(agent)
|
|
44
|
+
# DENY blocks the call with a structured denial message
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Governed GroupChat
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from ag2_tealtiger import GovernedGroupChat, TealTigerGuard, GovernanceMode
|
|
51
|
+
|
|
52
|
+
guard = TealTigerGuard(engine=engine, mode=GovernanceMode.ENFORCE)
|
|
53
|
+
group_chat = GovernedGroupChat(agents=[agent1, agent2, agent3], guard=guard)
|
|
54
|
+
speaker = group_chat.select_speaker(last_speaker=agent1)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Features
|
|
58
|
+
|
|
59
|
+
| Feature | Observe | Monitor | Enforce |
|
|
60
|
+
|---------|:-------:|:-------:|:-------:|
|
|
61
|
+
| Cost tracking per agent | ✅ | ✅ | ✅ |
|
|
62
|
+
| PII detection in tool args | ✅ | ✅ | ✅ |
|
|
63
|
+
| Structured audit entries (TEEC) | ✅ | ✅ | ✅ |
|
|
64
|
+
| Per-agent freeze/unfreeze | ✅ | ✅ | ✅ |
|
|
65
|
+
| Policy evaluation | — | ✅ (log) | ✅ (block) |
|
|
66
|
+
| Budget enforcement | — | ✅ (log) | ✅ (block) |
|
|
67
|
+
| REFER escalation | — | ✅ | ✅ |
|
|
68
|
+
| Inter-agent message governance | ✅ | ✅ | ✅ |
|
|
69
|
+
| Governed speaker selection | ✅ | ✅ | ✅ |
|
|
70
|
+
| JSONL audit export | ✅ | ✅ | ✅ |
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
Apache-2.0
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Example: GovernedGroupChat with Speaker Selection.
|
|
2
|
+
|
|
3
|
+
Demonstrates how GovernedGroupChat applies governance policies to speaker
|
|
4
|
+
selection in multi-agent conversations. In a governed group chat:
|
|
5
|
+
- Frozen agents are skipped without invoking TealEngine
|
|
6
|
+
- Over-budget agents are skipped without invoking TealEngine
|
|
7
|
+
- Remaining candidates are evaluated via TealEngine before getting a turn
|
|
8
|
+
- If all speakers are denied, the round terminates with ALL_SPEAKERS_DENIED
|
|
9
|
+
- REFER decisions suspend an agent while others continue
|
|
10
|
+
|
|
11
|
+
This is the pattern for multi-agent orchestration with governance guardrails.
|
|
12
|
+
|
|
13
|
+
Requirements:
|
|
14
|
+
pip install ag2-tealtiger
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
python governed_groupchat.py
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ─── Inline mocks so the example runs without real AG2/TealEngine ────────────
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MockConversableAgent:
|
|
29
|
+
"""Minimal AG2 ConversableAgent stub for demonstration."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, name: str, **kwargs):
|
|
32
|
+
self.name = name
|
|
33
|
+
self._reply_funcs: list[dict] = []
|
|
34
|
+
|
|
35
|
+
def register_reply(self, trigger=None, reply_func=None, position=0, **kwargs):
|
|
36
|
+
self._reply_funcs.insert(position, {
|
|
37
|
+
"trigger": trigger,
|
|
38
|
+
"reply_func": reply_func,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"Agent({self.name})"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class MockTealEngine:
|
|
46
|
+
"""Mock TealEngine with per-agent speaker selection policies.
|
|
47
|
+
|
|
48
|
+
Simulates governance evaluation for speaker candidacy. In production,
|
|
49
|
+
replace with a real TealEngine and configure policies that determine
|
|
50
|
+
which agents can speak based on role, context, or risk level.
|
|
51
|
+
|
|
52
|
+
from tealtiger import TealEngine
|
|
53
|
+
engine = TealEngine(policies=[speaker_policy, role_policy, ...])
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, denied_speakers: list[str] | None = None):
|
|
57
|
+
"""
|
|
58
|
+
Args:
|
|
59
|
+
denied_speakers: List of agent names that should be denied
|
|
60
|
+
when evaluated for speaker candidacy.
|
|
61
|
+
"""
|
|
62
|
+
self.denied_speakers = denied_speakers or []
|
|
63
|
+
self.evaluation_count = 0
|
|
64
|
+
|
|
65
|
+
def evaluate(
|
|
66
|
+
self,
|
|
67
|
+
tool_name: str = "",
|
|
68
|
+
tool_args: dict[str, Any] | None = None,
|
|
69
|
+
context: dict[str, Any] | None = None,
|
|
70
|
+
**kwargs: Any,
|
|
71
|
+
) -> dict[str, Any]:
|
|
72
|
+
"""Evaluate a speaker candidacy or tool call."""
|
|
73
|
+
self.evaluation_count += 1
|
|
74
|
+
agent_id = (context or {}).get("agent_id", "")
|
|
75
|
+
|
|
76
|
+
if agent_id in self.denied_speakers:
|
|
77
|
+
return {
|
|
78
|
+
"action": "DENY",
|
|
79
|
+
"reason": f"Agent '{agent_id}' not authorized to speak in this round",
|
|
80
|
+
"reason_codes": ["SPEAKER_DENIED", "ROLE_RESTRICTION"],
|
|
81
|
+
"risk_score": 60,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"action": "ALLOW",
|
|
86
|
+
"reason": "Agent authorized for this conversation round",
|
|
87
|
+
"reason_codes": ["SPEAKER_ALLOWED"],
|
|
88
|
+
"risk_score": 0,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# ─── Example: GovernedGroupChat Speaker Selection ────────────────────────────
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main() -> None:
|
|
96
|
+
"""Demonstrate GovernedGroupChat with governance-enforced speaker selection."""
|
|
97
|
+
from ag2_tealtiger import TealTigerGuard, GovernedGroupChat, GovernanceMode
|
|
98
|
+
|
|
99
|
+
print("=" * 65)
|
|
100
|
+
print(" ag2-tealtiger: GovernedGroupChat Speaker Selection")
|
|
101
|
+
print("=" * 65)
|
|
102
|
+
|
|
103
|
+
# ── Setup: Create agents for a coding team ────────────────────────────
|
|
104
|
+
planner = MockConversableAgent(name="planner")
|
|
105
|
+
coder = MockConversableAgent(name="coder")
|
|
106
|
+
reviewer = MockConversableAgent(name="reviewer")
|
|
107
|
+
executor = MockConversableAgent(name="executor")
|
|
108
|
+
|
|
109
|
+
# Create engine that denies the executor from speaking (role restriction)
|
|
110
|
+
engine = MockTealEngine(denied_speakers=["executor"])
|
|
111
|
+
|
|
112
|
+
# Create guard in ENFORCE mode
|
|
113
|
+
guard = TealTigerGuard(engine=engine, mode=GovernanceMode.ENFORCE)
|
|
114
|
+
|
|
115
|
+
# Attach guard to all agents (for tool call governance)
|
|
116
|
+
for agent in [planner, coder, reviewer, executor]:
|
|
117
|
+
guard.attach(agent)
|
|
118
|
+
|
|
119
|
+
# Create the governed group chat
|
|
120
|
+
group_chat = GovernedGroupChat(
|
|
121
|
+
agents=[planner, coder, reviewer, executor],
|
|
122
|
+
guard=guard,
|
|
123
|
+
max_round=10,
|
|
124
|
+
speaker_selection_method="round_robin",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# ── Scenario 1: Normal speaker selection with policy filtering ────────
|
|
128
|
+
print("\n--- Scenario 1: Policy-based speaker selection ---\n")
|
|
129
|
+
|
|
130
|
+
selected = group_chat.select_speaker(last_speaker=planner)
|
|
131
|
+
print(f" After planner spoke → selected: {selected}")
|
|
132
|
+
print(f" (executor is denied by policy, so it gets skipped)")
|
|
133
|
+
|
|
134
|
+
selected = group_chat.select_speaker(last_speaker=coder)
|
|
135
|
+
print(f" After coder spoke → selected: {selected}")
|
|
136
|
+
|
|
137
|
+
# ── Scenario 2: Frozen agent is skipped without engine call ───────────
|
|
138
|
+
print("\n--- Scenario 2: Frozen agent skipped ---\n")
|
|
139
|
+
|
|
140
|
+
guard.freeze("coder")
|
|
141
|
+
print(f" Froze 'coder' — is_frozen: {guard.is_frozen('coder')}")
|
|
142
|
+
|
|
143
|
+
# Engine should NOT be called for frozen agents
|
|
144
|
+
engine.evaluation_count = 0
|
|
145
|
+
selected = group_chat.select_speaker(last_speaker=planner)
|
|
146
|
+
print(f" Selected speaker: {selected}")
|
|
147
|
+
print(f" Engine evaluations: {engine.evaluation_count} (frozen agent skipped)")
|
|
148
|
+
|
|
149
|
+
guard.unfreeze("coder")
|
|
150
|
+
print(f" Unfroze 'coder'")
|
|
151
|
+
|
|
152
|
+
# ── Scenario 3: Over-budget agent is skipped ──────────────────────────
|
|
153
|
+
print("\n--- Scenario 3: Over-budget agent skipped ---\n")
|
|
154
|
+
|
|
155
|
+
# Set a low budget for reviewer and exceed it
|
|
156
|
+
guard.set_budget("reviewer", limit=0.0001)
|
|
157
|
+
# Simulate cost accumulation to exceed budget
|
|
158
|
+
guard._budget_manager.track_cost("reviewer", 0.001)
|
|
159
|
+
|
|
160
|
+
engine.evaluation_count = 0
|
|
161
|
+
selected = group_chat.select_speaker(last_speaker=planner)
|
|
162
|
+
print(f" Selected speaker: {selected}")
|
|
163
|
+
print(f" (reviewer over budget, executor denied by policy)")
|
|
164
|
+
|
|
165
|
+
# Check the speaker selection audit
|
|
166
|
+
audit = group_chat.speaker_selection_audit
|
|
167
|
+
latest = audit[-1] if audit else None
|
|
168
|
+
if latest:
|
|
169
|
+
print(f"\n Speaker selection audit (last round):")
|
|
170
|
+
for candidate in latest.candidates_evaluated:
|
|
171
|
+
print(
|
|
172
|
+
f" {candidate['agent_id']:<10} → "
|
|
173
|
+
f"{candidate['decision']:<6} "
|
|
174
|
+
f"reason={candidate.get('reason', 'N/A')}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# ── Scenario 4: All speakers denied → round terminates ───────────────
|
|
178
|
+
print("\n--- Scenario 4: All speakers denied ---\n")
|
|
179
|
+
|
|
180
|
+
# Freeze everyone except executor (who is policy-denied)
|
|
181
|
+
guard.freeze("planner")
|
|
182
|
+
guard.freeze("coder")
|
|
183
|
+
# reviewer is already over budget
|
|
184
|
+
|
|
185
|
+
selected = group_chat.select_speaker(last_speaker=planner)
|
|
186
|
+
print(f" Selected speaker: {selected}")
|
|
187
|
+
print(f" (All agents either frozen, over-budget, or policy-denied)")
|
|
188
|
+
|
|
189
|
+
# Check the termination audit entry
|
|
190
|
+
audit = group_chat.speaker_selection_audit
|
|
191
|
+
latest = audit[-1] if audit else None
|
|
192
|
+
if latest:
|
|
193
|
+
print(f" Reason codes: {latest.reason_codes}")
|
|
194
|
+
|
|
195
|
+
# Clean up freezes
|
|
196
|
+
guard.unfreeze("planner")
|
|
197
|
+
guard.unfreeze("coder")
|
|
198
|
+
guard.reset_budget("reviewer")
|
|
199
|
+
|
|
200
|
+
# ── Scenario 5: Full audit trail across selection rounds ──────────────
|
|
201
|
+
print("\n--- Speaker Selection Audit Trail ---\n")
|
|
202
|
+
|
|
203
|
+
all_audits = group_chat.speaker_selection_audit
|
|
204
|
+
print(f" Total selection rounds: {len(all_audits)}")
|
|
205
|
+
for i, entry in enumerate(all_audits, 1):
|
|
206
|
+
print(
|
|
207
|
+
f" Round {i}: selected={entry.selected_speaker or 'NONE':<10} "
|
|
208
|
+
f"candidates={len(entry.candidates_evaluated)} "
|
|
209
|
+
f"codes={entry.reason_codes}"
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# ── Key takeaways ─────────────────────────────────────────────────────
|
|
213
|
+
print("\n" + "=" * 65)
|
|
214
|
+
print(" Key points:")
|
|
215
|
+
print(" • GovernedGroupChat evaluates speakers against policies")
|
|
216
|
+
print(" • Frozen/over-budget agents skipped without engine calls")
|
|
217
|
+
print(" • Policy-denied agents skipped, next candidate evaluated")
|
|
218
|
+
print(" • ALL_SPEAKERS_DENIED terminates the round safely")
|
|
219
|
+
print(" • Full speaker selection audit trail per round")
|
|
220
|
+
print(" • Per-agent governance isolation in multi-agent groups")
|
|
221
|
+
print("=" * 65)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
if __name__ == "__main__":
|
|
225
|
+
main()
|