agentgear-ai 0.1.16__py3-none-any.whl

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.
Files changed (44) hide show
  1. agentgear/__init__.py +18 -0
  2. agentgear/cli/__init__.py +1 -0
  3. agentgear/cli/main.py +125 -0
  4. agentgear/sdk/__init__.py +6 -0
  5. agentgear/sdk/client.py +276 -0
  6. agentgear/sdk/decorators.py +65 -0
  7. agentgear/sdk/integrations/openai.py +52 -0
  8. agentgear/sdk/prompt.py +23 -0
  9. agentgear/sdk/trace.py +59 -0
  10. agentgear/server/__init__.py +1 -0
  11. agentgear/server/app/__init__.py +1 -0
  12. agentgear/server/app/api/__init__.py +1 -0
  13. agentgear/server/app/api/auth.py +156 -0
  14. agentgear/server/app/api/datasets.py +185 -0
  15. agentgear/server/app/api/evaluations.py +69 -0
  16. agentgear/server/app/api/evaluators.py +157 -0
  17. agentgear/server/app/api/llm_models.py +39 -0
  18. agentgear/server/app/api/metrics.py +18 -0
  19. agentgear/server/app/api/projects.py +139 -0
  20. agentgear/server/app/api/prompts.py +227 -0
  21. agentgear/server/app/api/runs.py +75 -0
  22. agentgear/server/app/api/seed.py +106 -0
  23. agentgear/server/app/api/settings.py +135 -0
  24. agentgear/server/app/api/spans.py +56 -0
  25. agentgear/server/app/api/tokens.py +67 -0
  26. agentgear/server/app/api/users.py +116 -0
  27. agentgear/server/app/auth.py +80 -0
  28. agentgear/server/app/config.py +26 -0
  29. agentgear/server/app/db.py +41 -0
  30. agentgear/server/app/deps.py +46 -0
  31. agentgear/server/app/main.py +77 -0
  32. agentgear/server/app/migrations.py +88 -0
  33. agentgear/server/app/models.py +339 -0
  34. agentgear/server/app/schemas.py +343 -0
  35. agentgear/server/app/utils/email.py +30 -0
  36. agentgear/server/app/utils/llm.py +27 -0
  37. agentgear/server/static/assets/index-BAAzXAln.js +121 -0
  38. agentgear/server/static/assets/index-CE45MZx1.css +1 -0
  39. agentgear/server/static/index.html +13 -0
  40. agentgear_ai-0.1.16.dist-info/METADATA +387 -0
  41. agentgear_ai-0.1.16.dist-info/RECORD +44 -0
  42. agentgear_ai-0.1.16.dist-info/WHEEL +4 -0
  43. agentgear_ai-0.1.16.dist-info/entry_points.txt +2 -0
  44. agentgear_ai-0.1.16.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,339 @@
1
+ import uuid
2
+ from datetime import datetime
3
+
4
+ from sqlalchemy import (
5
+ Boolean,
6
+ Column,
7
+ DateTime,
8
+ Float,
9
+ ForeignKey,
10
+ Integer,
11
+ JSON,
12
+ String,
13
+ Text,
14
+ func,
15
+ )
16
+ from sqlalchemy.orm import relationship
17
+
18
+ from agentgear.server.app.db import Base
19
+
20
+
21
+ def _uuid() -> str:
22
+ return str(uuid.uuid4())
23
+
24
+
25
+ class Project(Base):
26
+ __tablename__ = "projects"
27
+
28
+ id = Column(String, primary_key=True, default=_uuid)
29
+ name = Column(String, nullable=False, unique=True)
30
+ description = Column(Text, nullable=True)
31
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
32
+
33
+ prompts = relationship("Prompt", back_populates="project", cascade="all, delete-orphan")
34
+ tokens = relationship("APIKey", back_populates="project", cascade="all, delete-orphan")
35
+ runs = relationship("Run", back_populates="project", cascade="all, delete-orphan")
36
+ users = relationship("User", back_populates="project")
37
+ traces = relationship("Trace", back_populates="project", cascade="all, delete-orphan")
38
+ datasets = relationship("Dataset", back_populates="project", cascade="all, delete-orphan")
39
+ evaluations = relationship("Evaluation", back_populates="project", cascade="all, delete-orphan")
40
+ smtp_settings = relationship("SMTPSettings", back_populates="project", cascade="all, delete-orphan", uselist=False)
41
+ alert_rules = relationship("AlertRule", back_populates="project", cascade="all, delete-orphan")
42
+ metric_aggregates = relationship("MetricAggregate", back_populates="project", cascade="all, delete-orphan")
43
+
44
+
45
+ class APIKey(Base):
46
+ __tablename__ = "api_keys"
47
+
48
+ id = Column(String, primary_key=True, default=_uuid)
49
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
50
+ key_hash = Column(String, nullable=False, unique=True, index=True)
51
+ scopes = Column(JSON, nullable=False, default=list)
52
+ role = Column(String, default="user", nullable=False)
53
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
54
+ revoked = Column(Boolean, default=False, nullable=False)
55
+ last_used_at = Column(DateTime(timezone=True), nullable=True)
56
+
57
+ project = relationship("Project", back_populates="tokens")
58
+
59
+
60
+ class Prompt(Base):
61
+ __tablename__ = "prompts"
62
+
63
+ id = Column(String, primary_key=True, default=_uuid)
64
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
65
+ name = Column(String, nullable=False)
66
+ description = Column(Text, nullable=True)
67
+ scope = Column(String, default="project", nullable=False) # global vs project
68
+ tags = Column(JSON, nullable=True)
69
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
70
+
71
+ project = relationship("Project", back_populates="prompts")
72
+ versions = relationship("PromptVersion", back_populates="prompt", cascade="all, delete-orphan")
73
+
74
+
75
+ class PromptVersion(Base):
76
+ __tablename__ = "prompt_versions"
77
+
78
+ id = Column(String, primary_key=True, default=_uuid)
79
+ prompt_id = Column(String, ForeignKey("prompts.id"), nullable=False, index=True)
80
+ version = Column(Integer, nullable=False)
81
+ content = Column(Text, nullable=False)
82
+ metadata_ = Column("metadata", JSON, nullable=True) # "metadata" attribute name is reserved
83
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
84
+
85
+ prompt = relationship("Prompt", back_populates="versions")
86
+
87
+
88
+ class Trace(Base):
89
+ __tablename__ = "traces"
90
+
91
+ id = Column(String, primary_key=True, default=_uuid)
92
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
93
+ prompt_version_id = Column(String, ForeignKey("prompt_versions.id"), nullable=True, index=True)
94
+ name = Column(String, nullable=True)
95
+ status = Column(String, nullable=True)
96
+ input_text = Column(Text, nullable=True)
97
+ output_text = Column(Text, nullable=True)
98
+ model = Column(String, nullable=True)
99
+ request_payload = Column(JSON, nullable=True)
100
+ response_payload = Column(JSON, nullable=True)
101
+ token_input = Column(Integer, nullable=True)
102
+ token_output = Column(Integer, nullable=True)
103
+ cost = Column(Float, nullable=True)
104
+ latency_ms = Column(Float, nullable=True)
105
+ error = Column(Text, nullable=True)
106
+ error_stack = Column(Text, nullable=True)
107
+ tags = Column(JSON, nullable=True)
108
+ metadata_ = Column("metadata", JSON, nullable=True)
109
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
110
+
111
+ project = relationship("Project", back_populates="traces")
112
+ prompt_version = relationship("PromptVersion")
113
+
114
+
115
+ class Run(Base):
116
+ __tablename__ = "runs"
117
+
118
+ id = Column(String, primary_key=True, default=_uuid)
119
+ trace_id = Column(String, ForeignKey("traces.id"), nullable=True, index=True)
120
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
121
+ prompt_version_id = Column(String, ForeignKey("prompt_versions.id"), nullable=True, index=True)
122
+ name = Column(String, nullable=True)
123
+ status = Column(String, nullable=True)
124
+ input_text = Column(Text, nullable=True)
125
+ output_text = Column(Text, nullable=True)
126
+ model = Column(String, nullable=True)
127
+ request_payload = Column(JSON, nullable=True)
128
+ response_payload = Column(JSON, nullable=True)
129
+ token_input = Column(Integer, nullable=True)
130
+ token_output = Column(Integer, nullable=True)
131
+ cost = Column(Float, nullable=True)
132
+ latency_ms = Column(Float, nullable=True)
133
+ error = Column(Text, nullable=True)
134
+ error_stack = Column(Text, nullable=True)
135
+ tags = Column(JSON, nullable=True)
136
+ metadata_ = Column("metadata", JSON, nullable=True) # "metadata" attribute name is reserved
137
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
138
+
139
+ trace = relationship("Trace")
140
+ project = relationship("Project", back_populates="runs")
141
+ prompt_version = relationship("PromptVersion")
142
+ spans = relationship("Span", back_populates="run", cascade="all, delete-orphan")
143
+
144
+
145
+ class Span(Base):
146
+ __tablename__ = "spans"
147
+
148
+ id = Column(String, primary_key=True, default=_uuid)
149
+ trace_id = Column(String, ForeignKey("traces.id"), nullable=True, index=True)
150
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
151
+ run_id = Column(String, ForeignKey("runs.id"), nullable=False, index=True)
152
+ parent_id = Column(String, ForeignKey("spans.id"), nullable=True, index=True)
153
+ name = Column(String, nullable=False)
154
+ start_time = Column(DateTime(timezone=True), default=datetime.utcnow, nullable=False)
155
+ end_time = Column(DateTime(timezone=True), nullable=True)
156
+ latency_ms = Column(Float, nullable=True)
157
+ status = Column(String, nullable=True)
158
+ model = Column(String, nullable=True)
159
+ request_payload = Column(JSON, nullable=True)
160
+ response_payload = Column(JSON, nullable=True)
161
+ token_input = Column(Integer, nullable=True)
162
+ token_output = Column(Integer, nullable=True)
163
+ cost = Column(Float, nullable=True)
164
+ error = Column(Text, nullable=True)
165
+ error_stack = Column(Text, nullable=True)
166
+ tags = Column(JSON, nullable=True)
167
+ metadata_ = Column("metadata", JSON, nullable=True) # "metadata" attribute name is reserved
168
+
169
+ trace = relationship("Trace")
170
+ run = relationship("Run", back_populates="spans")
171
+ parent = relationship("Span", remote_side=[id])
172
+
173
+
174
+ class User(Base):
175
+ __tablename__ = "users"
176
+
177
+ id = Column(String, primary_key=True, default=_uuid)
178
+ project_id = Column(String, ForeignKey("projects.id"), nullable=True, index=True)
179
+ username = Column(String, unique=True, nullable=False, index=True)
180
+ email = Column(String, unique=True, nullable=True)
181
+ password_hash = Column(String, nullable=False)
182
+ salt = Column(String, nullable=False)
183
+ role = Column(String, default="user", nullable=False) # admin, user
184
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
185
+ # can access multiple projects? For now, simplistic single project or global admin
186
+
187
+ project = relationship("Project", back_populates="users")
188
+
189
+
190
+ class LLMModel(Base):
191
+ __tablename__ = "llm_models"
192
+
193
+ id = Column(String, primary_key=True, default=_uuid)
194
+ name = Column(String, nullable=False)
195
+ provider = Column(String, nullable=False) # openai, anthropic, etc.
196
+ api_key = Column(String, nullable=True) # encrypted/masked? Storing raw for now for simplicity in this task
197
+ base_url = Column(String, nullable=True)
198
+ config = Column(JSON, nullable=True)
199
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
200
+
201
+
202
+ class AdminUser(Base):
203
+ __tablename__ = "admin_users"
204
+
205
+ id = Column(String, primary_key=True, default=_uuid)
206
+ username = Column(String, unique=True, nullable=False, index=True)
207
+ password_hash = Column(String, nullable=False)
208
+ salt = Column(String, nullable=False)
209
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
210
+
211
+ class Evaluation(Base):
212
+ __tablename__ = "evaluations"
213
+
214
+ id = Column(String, primary_key=True, default=_uuid)
215
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
216
+ trace_id = Column(String, ForeignKey("traces.id"), nullable=True, index=True)
217
+ run_id = Column(String, ForeignKey("runs.id"), nullable=True, index=True)
218
+ span_id = Column(String, ForeignKey("spans.id"), nullable=True, index=True)
219
+ evaluator_type = Column(String, nullable=False) # human, rule, model, etc.
220
+ score = Column(Float, nullable=True)
221
+ max_score = Column(Float, nullable=True)
222
+ passed = Column(Boolean, nullable=True)
223
+ comments = Column(Text, nullable=True)
224
+ metadata_ = Column("metadata", JSON, nullable=True)
225
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
226
+
227
+ project = relationship("Project", back_populates="evaluations")
228
+ trace = relationship("Trace")
229
+ run = relationship("Run")
230
+ span = relationship("Span")
231
+
232
+
233
+ class Evaluator(Base):
234
+ __tablename__ = "evaluators"
235
+
236
+ id = Column(String, primary_key=True, default=_uuid)
237
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
238
+ name = Column(String, nullable=False)
239
+ prompt_template = Column(Text, nullable=False)
240
+ model = Column(String, nullable=False)
241
+ config = Column(JSON, nullable=True)
242
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
243
+
244
+ project = relationship("Project")
245
+
246
+
247
+ class Dataset(Base):
248
+ __tablename__ = "datasets"
249
+
250
+ id = Column(String, primary_key=True, default=_uuid)
251
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
252
+ name = Column(String, nullable=False)
253
+ description = Column(Text, nullable=True)
254
+ tags = Column(JSON, nullable=True)
255
+ metadata_ = Column("metadata", JSON, nullable=True)
256
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
257
+
258
+ project = relationship("Project", back_populates="datasets")
259
+ examples = relationship("DatasetExample", back_populates="dataset", cascade="all, delete-orphan")
260
+
261
+
262
+ class DatasetExample(Base):
263
+ __tablename__ = "dataset_examples"
264
+
265
+ id = Column(String, primary_key=True, default=_uuid)
266
+ dataset_id = Column(String, ForeignKey("datasets.id"), nullable=False, index=True)
267
+ input_text = Column(Text, nullable=True)
268
+ expected_output = Column(Text, nullable=True)
269
+ metadata_ = Column("metadata", JSON, nullable=True)
270
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
271
+
272
+ dataset = relationship("Dataset", back_populates="examples")
273
+
274
+
275
+ class SMTPSettings(Base):
276
+ __tablename__ = "smtp_settings"
277
+
278
+ id = Column(String, primary_key=True, default=_uuid)
279
+ project_id = Column(String, ForeignKey("projects.id"), nullable=True, index=True)
280
+ host = Column(String, nullable=False)
281
+ port = Column(Integer, nullable=False)
282
+ username = Column(String, nullable=True)
283
+ password = Column(String, nullable=True)
284
+ encryption = Column(String, nullable=True) # ssl, tls, starttls
285
+ sender_email = Column(String, nullable=True)
286
+ enabled = Column(Boolean, default=True, nullable=False)
287
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
288
+ updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False)
289
+
290
+ project = relationship("Project", back_populates="smtp_settings")
291
+
292
+
293
+ class AlertRule(Base):
294
+ __tablename__ = "alert_rules"
295
+
296
+ id = Column(String, primary_key=True, default=_uuid)
297
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
298
+ metric = Column(String, nullable=False)
299
+ threshold = Column(Float, nullable=False)
300
+ operator = Column(String, nullable=False, default=">")
301
+ window_minutes = Column(Integer, nullable=False, default=5)
302
+ recipients = Column(JSON, nullable=True)
303
+ enabled = Column(Boolean, default=True, nullable=False)
304
+ metadata_ = Column("metadata", JSON, nullable=True)
305
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
306
+
307
+ project = relationship("Project", back_populates="alert_rules")
308
+
309
+
310
+ class AlertEvent(Base):
311
+ __tablename__ = "alert_events"
312
+
313
+ id = Column(String, primary_key=True, default=_uuid)
314
+ rule_id = Column(String, ForeignKey("alert_rules.id"), nullable=False, index=True)
315
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
316
+ metric = Column(String, nullable=False)
317
+ value = Column(Float, nullable=True)
318
+ threshold = Column(Float, nullable=True)
319
+ traces = Column(JSON, nullable=True)
320
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
321
+
322
+ rule = relationship("AlertRule")
323
+ project = relationship("Project")
324
+
325
+
326
+ class MetricAggregate(Base):
327
+ __tablename__ = "metric_aggregates"
328
+
329
+ id = Column(String, primary_key=True, default=_uuid)
330
+ project_id = Column(String, ForeignKey("projects.id"), nullable=False, index=True)
331
+ metric = Column(String, nullable=False)
332
+ granularity = Column(String, nullable=False) # e.g., minute, hour, day
333
+ period_start = Column(DateTime(timezone=True), nullable=False)
334
+ period_end = Column(DateTime(timezone=True), nullable=False)
335
+ dimensions = Column(JSON, nullable=True) # e.g., model, prompt_version, span_type
336
+ values = Column(JSON, nullable=True) # e.g., avg, p95, p99, count, sum
337
+ created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
338
+
339
+ project = relationship("Project", back_populates="metric_aggregates")
@@ -0,0 +1,343 @@
1
+ from datetime import datetime
2
+ from typing import Any, List, Optional, Literal
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ # ... (Previous schemas) ...
7
+
8
+ class ORMModel(BaseModel):
9
+ class Config:
10
+ from_attributes = True
11
+ populate_by_name = True
12
+
13
+
14
+ class ProjectCreate(BaseModel):
15
+ name: str
16
+ description: Optional[str] = None
17
+
18
+
19
+ class ProjectRead(ORMModel):
20
+ id: str
21
+ name: str
22
+ description: Optional[str] = None
23
+ created_at: datetime
24
+
25
+
26
+ class TokenCreate(BaseModel):
27
+ scopes: List[str] = Field(
28
+ default_factory=lambda: ["runs.write", "prompts.read", "prompts.write", "tokens.manage", "datasets.read", "datasets.write", "evaluations.read", "evaluations.write"]
29
+ )
30
+
31
+
32
+ class TokenRead(ORMModel):
33
+ id: str
34
+ project_id: str
35
+ scopes: List[str]
36
+ created_at: datetime
37
+ revoked: bool
38
+ last_used_at: Optional[datetime] = None
39
+
40
+
41
+ class TokenWithSecret(TokenRead):
42
+ token: str
43
+
44
+
45
+ class PromptCreate(BaseModel):
46
+ project_id: str
47
+ name: str
48
+ description: Optional[str] = None
49
+ scope: Optional[str] = "project"
50
+ content: str
51
+ tags: Optional[List[str]] = None
52
+ metadata: Optional[dict[str, Any]] = None
53
+
54
+
55
+ class PromptUpdate(BaseModel):
56
+ name: Optional[str] = None
57
+ description: Optional[str] = None
58
+ tags: Optional[List[str]] = None
59
+
60
+
61
+ class PromptVersionCreate(BaseModel):
62
+ content: str
63
+ metadata: Optional[dict[str, Any]] = None
64
+
65
+
66
+ class PromptRead(ORMModel):
67
+ id: str
68
+ project_id: str
69
+ name: str
70
+ description: Optional[str] = None
71
+ scope: str
72
+ tags: Optional[List[str]] = None
73
+ created_at: datetime
74
+
75
+
76
+ class PromptVersionRead(ORMModel):
77
+ id: str
78
+ prompt_id: str
79
+ version: int
80
+ content: str
81
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
82
+ created_at: datetime
83
+
84
+
85
+ class TokenUsage(BaseModel):
86
+ prompt: Optional[int] = None
87
+ completion: Optional[int] = None
88
+ total: Optional[int] = None
89
+
90
+
91
+ class PromptRunRequest(BaseModel):
92
+ version_id: Optional[str] = None # if None, latest
93
+ inputs: dict[str, str] = {}
94
+ model_config_name: Optional[str] = None # ID or name of LLMModel
95
+ stream: bool = False
96
+
97
+
98
+ class PromptRunResponse(BaseModel):
99
+ output: str
100
+ error: Optional[str] = None
101
+ latency_ms: float
102
+ token_usage: Optional[TokenUsage] = None
103
+
104
+
105
+
106
+
107
+
108
+ class RunCreate(BaseModel):
109
+ project_id: str
110
+ prompt_version_id: Optional[str] = None
111
+ name: Optional[str] = None
112
+ input_text: Optional[str] = None
113
+ output_text: Optional[str] = None
114
+ token_usage: Optional[TokenUsage] = None
115
+ cost: Optional[float] = None
116
+ latency_ms: Optional[float] = None
117
+ error: Optional[str] = None
118
+ metadata: Optional[dict[str, Any]] = None
119
+
120
+
121
+ class RunRead(ORMModel):
122
+ id: str
123
+ project_id: str
124
+ prompt_version_id: Optional[str]
125
+ name: Optional[str]
126
+ input_text: Optional[str]
127
+ output_text: Optional[str]
128
+ token_input: Optional[int]
129
+ token_output: Optional[int]
130
+ cost: Optional[float]
131
+ latency_ms: Optional[float]
132
+ error: Optional[str]
133
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
134
+ created_at: datetime
135
+
136
+
137
+ class SpanCreate(BaseModel):
138
+ project_id: str
139
+ run_id: str
140
+ parent_id: Optional[str] = None
141
+ name: str
142
+ start_time: Optional[datetime] = None
143
+ end_time: Optional[datetime] = None
144
+ latency_ms: Optional[float] = None
145
+ metadata: Optional[dict[str, Any]] = None
146
+
147
+
148
+ class SpanRead(ORMModel):
149
+ id: str
150
+ project_id: str
151
+ run_id: str
152
+ parent_id: Optional[str]
153
+ name: str
154
+ start_time: datetime
155
+ end_time: Optional[datetime]
156
+ latency_ms: Optional[float]
157
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
158
+
159
+
160
+ class MetricsSummary(BaseModel):
161
+ runs: int
162
+ spans: int
163
+ prompts: int
164
+ projects: int
165
+
166
+
167
+ class AuthStatus(BaseModel):
168
+ configured: bool
169
+ mode: Literal["none", "env", "db"]
170
+ username: Optional[str] = None
171
+ project_id: Optional[str] = None
172
+
173
+
174
+ class AuthSetup(BaseModel):
175
+ username: str
176
+ password: str
177
+
178
+
179
+ class AuthLogin(BaseModel):
180
+ username: str
181
+ password: str
182
+
183
+
184
+ class AuthResponse(BaseModel):
185
+ token: str
186
+ project_id: str
187
+ username: str
188
+ role: str = "user"
189
+
190
+
191
+ class UserCreate(BaseModel):
192
+ username: str
193
+ password: str
194
+ email: Optional[str] = None
195
+ project_id: Optional[str] = None
196
+ role: str = "user"
197
+
198
+
199
+ class UserRead(ORMModel):
200
+ id: str
201
+ username: str
202
+ email: Optional[str]
203
+ project_id: Optional[str]
204
+ role: str
205
+ created_at: datetime
206
+
207
+
208
+ class UserPasswordReset(BaseModel):
209
+ password: str
210
+
211
+
212
+ class LLMModelCreate(BaseModel):
213
+ name: str
214
+ provider: str
215
+ api_key: Optional[str] = None
216
+ base_url: Optional[str] = None
217
+ config: Optional[dict[str, Any]] = None
218
+
219
+
220
+ class LLMModelRead(ORMModel):
221
+ id: str
222
+ name: str
223
+ provider: str
224
+ base_url: Optional[str]
225
+ config: Optional[dict[str, Any]]
226
+ created_at: datetime
227
+
228
+ # --- NEW SCHEMAS FOR SETTINGS ---
229
+
230
+ class AlertRuleCreate(BaseModel):
231
+ project_id: str
232
+ metric: str
233
+ threshold: float
234
+ operator: str = ">"
235
+ window_minutes: int = 5
236
+ recipients: List[str] # List of emails
237
+ enabled: bool = True
238
+
239
+ class AlertRuleRead(ORMModel):
240
+ id: str
241
+ project_id: str
242
+ metric: str
243
+ threshold: float
244
+ operator: str
245
+ window_minutes: int
246
+ recipients: Optional[List[str]]
247
+ enabled: bool
248
+ created_at: datetime
249
+
250
+ class SMTPConfig(BaseModel):
251
+ host: str
252
+ port: int
253
+ username: Optional[str] = None
254
+ password: Optional[str] = None
255
+ encryption: Optional[str] = None
256
+ sender_email: Optional[str] = None
257
+ enabled: bool = True
258
+
259
+ class RoleCreate(BaseModel):
260
+ name: str
261
+ permissions: List[str]
262
+
263
+ class RoleRead(BaseModel):
264
+ id: str
265
+ name: str
266
+ permissions: List[str]
267
+
268
+
269
+ # --- NEW SCHEMAS FOR DATASETS & EVALUATIONS ---
270
+
271
+ class DatasetCreate(BaseModel):
272
+ project_id: Optional[str] = None
273
+ name: str
274
+ description: Optional[str] = None
275
+ tags: Optional[List[str]] = None
276
+ metadata: Optional[dict[str, Any]] = None
277
+
278
+ class DatasetRead(ORMModel):
279
+ id: str
280
+ project_id: str
281
+ name: str
282
+ description: Optional[str] = None
283
+ tags: Optional[List[str]] = None
284
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
285
+ created_at: datetime
286
+
287
+ class DatasetExampleCreate(BaseModel):
288
+ input_text: Optional[str] = None
289
+ expected_output: Optional[str] = None
290
+ metadata: Optional[dict[str, Any]] = None
291
+
292
+ class DatasetExampleRead(ORMModel):
293
+ id: str
294
+ dataset_id: str
295
+ input_text: Optional[str] = None
296
+ expected_output: Optional[str] = None
297
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
298
+ created_at: datetime
299
+
300
+ class EvaluationCreate(BaseModel):
301
+ project_id: str
302
+ trace_id: Optional[str] = None
303
+ run_id: Optional[str] = None
304
+ span_id: Optional[str] = None
305
+ evaluator_type: str = "manual"
306
+ score: Optional[float] = None
307
+ max_score: Optional[float] = None
308
+ passed: Optional[bool] = None
309
+ comments: Optional[str] = None
310
+ metadata: Optional[dict[str, Any]] = None
311
+
312
+ class EvaluationRead(ORMModel):
313
+ id: str
314
+ project_id: str
315
+ trace_id: Optional[str]
316
+ run_id: Optional[str]
317
+ span_id: Optional[str]
318
+ evaluator_type: str
319
+ score: Optional[float]
320
+ max_score: Optional[float]
321
+ passed: Optional[bool]
322
+ comments: Optional[str]
323
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
324
+ metadata: Optional[dict[str, Any]] = Field(default=None, alias="metadata_")
325
+ created_at: datetime
326
+
327
+
328
+ class EvaluatorCreate(BaseModel):
329
+ project_id: Optional[str] = None
330
+ name: str
331
+ prompt_template: str
332
+ model: str
333
+ config: Optional[dict[str, Any]] = None
334
+
335
+ class EvaluatorRead(ORMModel):
336
+ id: str
337
+ project_id: str
338
+ name: str
339
+ prompt_template: str
340
+ model: str
341
+ config: Optional[dict[str, Any]]
342
+ created_at: datetime
343
+
@@ -0,0 +1,30 @@
1
+ import smtplib
2
+ from email.mime.text import MIMEText
3
+ from email.mime.multipart import MIMEMultipart
4
+ from typing import List, Optional
5
+
6
+ from agentgear.server.app.models import SMTPSettings
7
+
8
+ def send_email(smtp_config: SMTPSettings, recipients: List[str], subject: str, html_content: str):
9
+ if not smtp_config or not smtp_config.enabled:
10
+ raise ValueError("SMTP is not configured or enabled")
11
+
12
+ msg = MIMEMultipart()
13
+ msg["From"] = smtp_config.sender_email
14
+ msg["Subject"] = subject
15
+ msg.attach(MIMEText(html_content, "html"))
16
+
17
+ try:
18
+ server = smtplib.SMTP(smtp_config.host, smtp_config.port)
19
+ if smtp_config.encryption == "starttls":
20
+ server.starttls()
21
+ elif smtp_config.encryption == "ssl":
22
+ server = smtplib.SMTP_SSL(smtp_config.host, smtp_config.port)
23
+
24
+ if smtp_config.username and smtp_config.password:
25
+ server.login(smtp_config.username, smtp_config.password)
26
+
27
+ server.sendmail(smtp_config.sender_email, recipients, msg.as_string())
28
+ server.quit()
29
+ except Exception as e:
30
+ raise Exception(f"Failed to send email: {str(e)}")