-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.py
More file actions
152 lines (126 loc) · 4.81 KB
/
config.py
File metadata and controls
152 lines (126 loc) · 4.81 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
147
148
149
150
151
152
"""Configuration constants, model maps, and keyword lists.
This is the leaf module — it imports nothing from the project.
Every other module imports from here. Prompts live in prompts.py.
"""
from __future__ import annotations
import os
from pathlib import Path
from dotenv import load_dotenv
# Load .env from the project directory (not the cwd).
# override=True ensures .env values win even if the shell has empty exports.
load_dotenv(Path(__file__).resolve().parent / ".env", override=True)
# --- ANSI color codes (UI layer — swap these out when moving to a GUI) ---
DIM = "\033[2m"
BOLD = "\033[1m"
RESET = "\033[0m"
CYAN = "\033[36m"
GREEN = "\033[38;5;115m"
YELLOW = "\033[33m"
RED = "\033[31m"
MAGENTA = "\033[35m"
BLUE = "\033[38;5;111m"
def get_env(name: str, default: str | None = None, required: bool = False) -> str:
value = os.getenv(name, default)
if required and not value:
raise ValueError(f"Missing required environment variable: {name}")
return value or ""
# Keep last 82 entries (~41 exchanges) so remote models see full context
MAX_HISTORY = 82
# When user switches to a non-default local model via @mention,
# stick with that model for this many turns before reverting to default.
STICKY_LOCAL_MAX = 10
# Calendar: how many days ahead to fetch events
CALENDAR_LOOKAHEAD_DAYS = 7
# Keywords that trigger automatic calendar context injection
CALENDAR_KEYWORDS = [
"calendar", "schedule", "meeting", "meetings",
"free", "busy", "appointment", "what's on my",
"what do i have", "am i free", "my week", "my day",
"today's schedule", "tomorrow's schedule",
]
# Obsidian journal settings
OBSIDIAN_VAULT_PATH = get_env("OBSIDIAN_VAULT_PATH", "~/Documents/vault-zero")
JOURNAL_LOOKBACK_DAYS = 7
# Session memory — single file overwritten each session, deleted by /forget
SESSION_SUMMARY_PATH = Path(OBSIDIAN_VAULT_PATH).expanduser() / "System" / "session-summary.md"
# Keywords that trigger journal context injection
JOURNAL_KEYWORDS = [
"journal", "diary", "daily note", "daily notes",
"how am i doing", "how's my week", "how is my week",
"how was my week", "how's my day", "how was my day",
"what did i do", "what have i done", "what have i been doing",
"what did i work on", "what have i worked on",
"my progress", "my mood", "my energy",
"what got done", "what didn't get done",
"my habits", "my routine",
"my blockers", "what's blocking",
"review my week", "weekly review", "week review",
"how's it going", "how am i",
"workload", "overwhelmed", "burned out", "burnout",
"time management", "productivity",
"should i take", "can i handle", "am i overloaded",
]
# Keywords that trigger Canvas assignment context injection
CANVAS_KEYWORDS = [
"canvas", "assignment", "assignments", "due", "deadline",
"deadlines", "homework", "what's due", "whats due",
"school", "grades", "syllabus", "coursework",
]
# Planning / time-management keywords — trigger ALL context sources at once.
# Any match here fires calendar + journal + canvas, same as matching any
# individual keyword list above. Kept separate so the intent is clear.
PLANNING_KEYWORDS = [
"schedule", "plan my", "plan the", "plan this",
"organize my", "organize the", "prioritize",
"what should i", "what do i need", "what's on deck",
"whats on deck", "on deck", "on my plate", "triage",
"help me plan", "help me prioritize", "help me schedule",
"this week", "next week", "rest of my",
"tomorrow", "tonight", "time for",
"fit everything", "squeeze in", "make time",
"falling behind", "behind on", "catching up", "catch up",
"prep for", "prepare for", "get ready",
"overwhelmed", "swamped", "slammed", "stretched thin",
"how much time", "do i have time", "is there time",
]
# --- Model configuration ---
# Models that require the Responses API instead of Chat Completions
RESPONSES_API_MODELS = {"gpt-5.2-pro"}
MODEL_MAP = {
"HAIKU": "claude-haiku-4-5",
"SONNET": "claude-sonnet-4-6",
"OPUS": "claude-opus-4-6",
"GPT_MINI": "gpt-5-mini",
"GPT": "gpt-5.2",
"GPT_PRO": "gpt-5.2-pro",
}
# Which provider handles each model
MODEL_PROVIDER = {
"HAIKU": "anthropic",
"SONNET": "anthropic",
"OPUS": "anthropic",
"GPT_MINI": "openai",
"GPT": "openai",
"GPT_PRO": "openai",
}
VALID_NEXT_STEPS = {"RESPOND_LOCALLY", "SEND_TO_REMOTE", "ASK_USER"}
COST_INFO = {
"HAIKU": "cheap",
"SONNET": "moderate",
"OPUS": "expensive",
"GPT_MINI": "cheap",
"GPT": "moderate",
"GPT_PRO": "very expensive",
}
# Shortcuts the user can type at the Phone a Friend prompt or as @mentions
MODEL_SHORTCUTS = {
"haiku": "HAIKU",
"sonnet": "SONNET",
"opus": "OPUS",
"gpt": "GPT",
"mini": "GPT_MINI",
"gpt_mini": "GPT_MINI",
"gpt-mini": "GPT_MINI",
"gpt_pro": "GPT_PRO",
"gpt-pro": "GPT_PRO",
}