-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpartnership_cli.py
More file actions
executable file
·146 lines (124 loc) · 5.12 KB
/
partnership_cli.py
File metadata and controls
executable file
·146 lines (124 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env python3
"""
partnership CLI - Battle-test your decisions with adversarial agents.
Usage:
partnership design "Your problem" --mode adversarial
partnership design "Your problem" --mode cooperative
"""
import sys
import argparse
import time
sys.path.insert(0, 'agent')
from agent import Agent
from partnership import marry
from adversarial import marry_adversarial
from api_monitor import APIMonitor
from monitored_agent import MonitoredAgent
def estimate_cost(text, multiplier=1):
"""Rough cost estimate: $3/M tokens, ~4 chars per token"""
tokens = len(text) // 4 * multiplier
cost = tokens * 0.000003
return tokens, cost
def make_agent(persona, model, monitor=None):
if monitor:
a = MonitoredAgent(persona, model, monitor)
return lambda msg: a.run_sync(msg)
else:
a = Agent(persona, model=model)
return lambda msg: a.run_sync(msg)
def main():
parser = argparse.ArgumentParser(
description="Battle-test decisions with adversarial agents",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
partnership design "Should we use microservices or monolith?"
partnership design "Rate limiting strategy" --mode adversarial --rounds 2
partnership design "API design" --output design.md
"""
)
parser.add_argument("command", nargs='?', choices=["design"], help="Command to run")
parser.add_argument("problem", nargs='?', help="Problem or decision to analyze")
parser.add_argument("--mode", choices=["cooperative", "adversarial"],
default="adversarial", help="Protocol mode")
parser.add_argument("--model", default="anthropic:claude-sonnet-4-20250514",
help="Model to use")
parser.add_argument("--rounds", type=int, default=2,
help="Attack/defend rounds (adversarial only)")
parser.add_argument("--output", "-o", help="Save output to file")
parser.add_argument("--verbose", "-v", action="store_true",
help="Show full conversation")
parser.add_argument("--estimate-only", action="store_true",
help="Show cost estimate and exit")
parser.add_argument("--show-log", type=int, metavar="N",
help="Show last N API calls from log and exit")
parser.add_argument("--log-stats", action="store_true",
help="Show API stats from log and exit")
args = parser.parse_args()
# Handle log viewing commands
if args.show_log:
monitor = APIMonitor("api_monitor.jsonl")
monitor.tail_log(args.show_log)
return 0
if args.log_stats:
monitor = APIMonitor("api_monitor.jsonl")
# Need to reload stats from log file
import json
try:
with open("api_monitor.jsonl", 'r') as f:
for line in f:
entry = json.loads(line)
monitor.call_count += 1
if entry.get("status") == "error":
monitor.error_count += 1
if entry.get("status") == "rate_limited":
monitor.rate_limit_count += 1
monitor.total_tokens += entry.get("total_tokens", 0)
except FileNotFoundError:
print("No log file found yet")
return 1
monitor.print_stats()
return 0
# Cost estimate
tokens_est, cost_est = estimate_cost(args.problem,
multiplier=6 if args.mode == "adversarial" else 2)
print(f"🤖 Partnership CLI")
print(f"Mode: {args.mode}")
print(f"Est. cost: ~${cost_est:.4f} (~{tokens_est:,} tokens)")
if args.estimate_only:
return 0
print(f"\n{'='*70}")
print(f"PROBLEM: {args.problem}")
print(f"{'='*70}\n")
# Create monitor
monitor = APIMonitor("api_monitor.jsonl")
# Create agents
start = time.time()
if args.mode == "adversarial":
proposer = make_agent("You are a systems architect.", args.model, monitor)
attacker = make_agent("You are a security and performance expert.", args.model, monitor)
paired = marry_adversarial(proposer, attacker,
rounds=args.rounds, verbose=args.verbose)
else:
agent_a = make_agent("You are a systems architect.", args.model, monitor)
agent_b = make_agent("You are a systems architect.", args.model, monitor)
paired = marry(agent_a, agent_b, verbose=args.verbose)
# Run
result = paired(args.problem)
elapsed = time.time() - start
# Output
if not args.verbose:
print(result)
print(f"\n{'='*70}")
print(f"⏱️ Time: {elapsed:.1f}s")
print(f"💰 Est. cost: ${cost_est:.4f}")
# Show API monitoring stats
monitor.print_stats()
if args.output:
with open(args.output, 'w') as f:
f.write(f"# Problem\n\n{args.problem}\n\n")
f.write(f"# Solution ({args.mode})\n\n{result}\n")
print(f"📝 Saved to: {args.output}")
return 0
if __name__ == "__main__":
sys.exit(main())