idun-agent-schema 0.1.41__py3-none-any.whl → 0.1.42__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.
Potentially problematic release.
This version of idun-agent-schema might be problematic. Click here for more details.
- idun_agent_schema/__init__.py +10 -10
- idun_agent_schema/engine/__init__.py +11 -12
- idun_agent_schema/engine/agent.py +16 -16
- idun_agent_schema/engine/api.py +29 -29
- idun_agent_schema/engine/config.py +1 -5
- idun_agent_schema/engine/haystack.py +13 -13
- idun_agent_schema/engine/langgraph.py +47 -47
- idun_agent_schema/engine/server.py +15 -15
- idun_agent_schema/manager/__init__.py +50 -50
- idun_agent_schema/manager/api.py +158 -158
- idun_agent_schema/manager/deployments.py +12 -12
- idun_agent_schema/manager/deps.py +14 -14
- idun_agent_schema/manager/domain.py +276 -276
- idun_agent_schema/manager/dto.py +131 -131
- idun_agent_schema/manager/errors.py +22 -22
- idun_agent_schema/manager/settings.py +161 -161
- idun_agent_schema/shared/__init__.py +5 -5
- idun_agent_schema/shared/observability.py +56 -56
- {idun_agent_schema-0.1.41.dist-info → idun_agent_schema-0.1.42.dist-info}/METADATA +1 -1
- idun_agent_schema-0.1.42.dist-info/RECORD +22 -0
- idun_agent_schema-0.1.41.dist-info/RECORD +0 -22
- {idun_agent_schema-0.1.41.dist-info → idun_agent_schema-0.1.42.dist-info}/WHEEL +0 -0
idun_agent_schema/manager/dto.py
CHANGED
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
"""DTOs for Manager operations."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from typing import Any
|
|
5
|
-
from uuid import UUID
|
|
6
|
-
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
8
|
-
|
|
9
|
-
from .domain import AgentFramework, TenantPlan
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class AgentCreateDTO(BaseModel):
|
|
13
|
-
"""DTO for creating an agent in the application layer."""
|
|
14
|
-
|
|
15
|
-
name: str = Field(..., min_length=1, max_length=255)
|
|
16
|
-
description: str | None = Field(None, max_length=1000)
|
|
17
|
-
framework: AgentFramework
|
|
18
|
-
config: dict[str, Any] = Field(default_factory=dict)
|
|
19
|
-
environment_variables: dict[str, str] = Field(default_factory=dict)
|
|
20
|
-
tags: list[str] = Field(default_factory=list)
|
|
21
|
-
tenant_id: UUID
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class AgentUpdateDTO(BaseModel):
|
|
25
|
-
"""DTO for updating an existing agent."""
|
|
26
|
-
|
|
27
|
-
name: str | None = Field(None, min_length=1, max_length=255)
|
|
28
|
-
description: str | None = Field(None, max_length=1000)
|
|
29
|
-
config: dict[str, Any] | None = None
|
|
30
|
-
environment_variables: dict[str, str] | None = None
|
|
31
|
-
tags: list[str] | None = None
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class AgentDeploymentDTO(BaseModel):
|
|
35
|
-
"""DTO describing deployment details for an agent."""
|
|
36
|
-
|
|
37
|
-
agent_id: UUID
|
|
38
|
-
container_id: str
|
|
39
|
-
endpoint: str
|
|
40
|
-
status: str
|
|
41
|
-
framework: str
|
|
42
|
-
deployed_at: datetime | None = None
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class AgentHealthDTO(BaseModel):
|
|
46
|
-
"""DTO representing health metrics for an agent instance."""
|
|
47
|
-
|
|
48
|
-
agent_id: UUID
|
|
49
|
-
status: str
|
|
50
|
-
uptime: str | None = None
|
|
51
|
-
cpu_usage: str | None = None
|
|
52
|
-
memory_usage: str | None = None
|
|
53
|
-
last_activity: str | None = None
|
|
54
|
-
error: str | None = None
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class AgentRunCreateDTO(BaseModel):
|
|
58
|
-
"""DTO for creating a new agent run."""
|
|
59
|
-
|
|
60
|
-
agent_id: UUID
|
|
61
|
-
tenant_id: UUID
|
|
62
|
-
input_data: dict[str, Any]
|
|
63
|
-
trace_id: str | None = None
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class AgentMetricsDTO(BaseModel):
|
|
67
|
-
"""DTO for aggregated performance metrics of an agent."""
|
|
68
|
-
|
|
69
|
-
agent_id: UUID
|
|
70
|
-
total_runs: int
|
|
71
|
-
success_rate: float | None = Field(None, ge=0.0, le=1.0)
|
|
72
|
-
avg_response_time_ms: float | None = Field(None, ge=0)
|
|
73
|
-
last_run_at: datetime | None = None
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class TenantCreateDTO(BaseModel):
|
|
77
|
-
"""DTO for creating a tenant."""
|
|
78
|
-
|
|
79
|
-
name: str = Field(..., min_length=1, max_length=255)
|
|
80
|
-
slug: str = Field(..., min_length=1, max_length=100)
|
|
81
|
-
email: str = Field(..., description="Primary contact email")
|
|
82
|
-
website: str | None = None
|
|
83
|
-
plan: TenantPlan = Field(default=TenantPlan.FREE)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class TenantUpdateDTO(BaseModel):
|
|
87
|
-
"""DTO for updating tenant metadata and settings."""
|
|
88
|
-
|
|
89
|
-
name: str | None = Field(None, min_length=1, max_length=255)
|
|
90
|
-
email: str | None = None
|
|
91
|
-
website: str | None = None
|
|
92
|
-
settings: dict[str, Any] | None = None
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
class TenantUsageDTO(BaseModel):
|
|
96
|
-
"""DTO summarizing a tenant's resource usage and quotas."""
|
|
97
|
-
|
|
98
|
-
tenant_id: UUID
|
|
99
|
-
current_agents: int
|
|
100
|
-
max_agents: int
|
|
101
|
-
current_runs_this_month: int
|
|
102
|
-
max_runs_per_month: int
|
|
103
|
-
current_storage_mb: float
|
|
104
|
-
max_storage_mb: int
|
|
105
|
-
usage_percentage: dict[str, float]
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class TenantQuotaDTO(BaseModel):
|
|
109
|
-
"""DTO for updating tenant quotas."""
|
|
110
|
-
|
|
111
|
-
max_agents: int | None = None
|
|
112
|
-
max_runs_per_month: int | None = None
|
|
113
|
-
max_storage_mb: int | None = None
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
class TenantUserCreateDTO(BaseModel):
|
|
117
|
-
"""DTO for adding a user to a tenant."""
|
|
118
|
-
|
|
119
|
-
tenant_id: UUID
|
|
120
|
-
user_id: str
|
|
121
|
-
email: str
|
|
122
|
-
role: str = Field(default="member")
|
|
123
|
-
permissions: list[str] = Field(default_factory=list)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
class TenantUserUpdateDTO(BaseModel):
|
|
127
|
-
"""DTO for updating tenant user fields."""
|
|
128
|
-
|
|
129
|
-
role: str | None = None
|
|
130
|
-
permissions: list[str] | None = None
|
|
131
|
-
is_active: bool | None = None
|
|
1
|
+
"""DTOs for Manager operations."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from .domain import AgentFramework, TenantPlan
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentCreateDTO(BaseModel):
|
|
13
|
+
"""DTO for creating an agent in the application layer."""
|
|
14
|
+
|
|
15
|
+
name: str = Field(..., min_length=1, max_length=255)
|
|
16
|
+
description: str | None = Field(None, max_length=1000)
|
|
17
|
+
framework: AgentFramework
|
|
18
|
+
config: dict[str, Any] = Field(default_factory=dict)
|
|
19
|
+
environment_variables: dict[str, str] = Field(default_factory=dict)
|
|
20
|
+
tags: list[str] = Field(default_factory=list)
|
|
21
|
+
tenant_id: UUID
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AgentUpdateDTO(BaseModel):
|
|
25
|
+
"""DTO for updating an existing agent."""
|
|
26
|
+
|
|
27
|
+
name: str | None = Field(None, min_length=1, max_length=255)
|
|
28
|
+
description: str | None = Field(None, max_length=1000)
|
|
29
|
+
config: dict[str, Any] | None = None
|
|
30
|
+
environment_variables: dict[str, str] | None = None
|
|
31
|
+
tags: list[str] | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AgentDeploymentDTO(BaseModel):
|
|
35
|
+
"""DTO describing deployment details for an agent."""
|
|
36
|
+
|
|
37
|
+
agent_id: UUID
|
|
38
|
+
container_id: str
|
|
39
|
+
endpoint: str
|
|
40
|
+
status: str
|
|
41
|
+
framework: str
|
|
42
|
+
deployed_at: datetime | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AgentHealthDTO(BaseModel):
|
|
46
|
+
"""DTO representing health metrics for an agent instance."""
|
|
47
|
+
|
|
48
|
+
agent_id: UUID
|
|
49
|
+
status: str
|
|
50
|
+
uptime: str | None = None
|
|
51
|
+
cpu_usage: str | None = None
|
|
52
|
+
memory_usage: str | None = None
|
|
53
|
+
last_activity: str | None = None
|
|
54
|
+
error: str | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AgentRunCreateDTO(BaseModel):
|
|
58
|
+
"""DTO for creating a new agent run."""
|
|
59
|
+
|
|
60
|
+
agent_id: UUID
|
|
61
|
+
tenant_id: UUID
|
|
62
|
+
input_data: dict[str, Any]
|
|
63
|
+
trace_id: str | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AgentMetricsDTO(BaseModel):
|
|
67
|
+
"""DTO for aggregated performance metrics of an agent."""
|
|
68
|
+
|
|
69
|
+
agent_id: UUID
|
|
70
|
+
total_runs: int
|
|
71
|
+
success_rate: float | None = Field(None, ge=0.0, le=1.0)
|
|
72
|
+
avg_response_time_ms: float | None = Field(None, ge=0)
|
|
73
|
+
last_run_at: datetime | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TenantCreateDTO(BaseModel):
|
|
77
|
+
"""DTO for creating a tenant."""
|
|
78
|
+
|
|
79
|
+
name: str = Field(..., min_length=1, max_length=255)
|
|
80
|
+
slug: str = Field(..., min_length=1, max_length=100)
|
|
81
|
+
email: str = Field(..., description="Primary contact email")
|
|
82
|
+
website: str | None = None
|
|
83
|
+
plan: TenantPlan = Field(default=TenantPlan.FREE)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class TenantUpdateDTO(BaseModel):
|
|
87
|
+
"""DTO for updating tenant metadata and settings."""
|
|
88
|
+
|
|
89
|
+
name: str | None = Field(None, min_length=1, max_length=255)
|
|
90
|
+
email: str | None = None
|
|
91
|
+
website: str | None = None
|
|
92
|
+
settings: dict[str, Any] | None = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TenantUsageDTO(BaseModel):
|
|
96
|
+
"""DTO summarizing a tenant's resource usage and quotas."""
|
|
97
|
+
|
|
98
|
+
tenant_id: UUID
|
|
99
|
+
current_agents: int
|
|
100
|
+
max_agents: int
|
|
101
|
+
current_runs_this_month: int
|
|
102
|
+
max_runs_per_month: int
|
|
103
|
+
current_storage_mb: float
|
|
104
|
+
max_storage_mb: int
|
|
105
|
+
usage_percentage: dict[str, float]
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class TenantQuotaDTO(BaseModel):
|
|
109
|
+
"""DTO for updating tenant quotas."""
|
|
110
|
+
|
|
111
|
+
max_agents: int | None = None
|
|
112
|
+
max_runs_per_month: int | None = None
|
|
113
|
+
max_storage_mb: int | None = None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class TenantUserCreateDTO(BaseModel):
|
|
117
|
+
"""DTO for adding a user to a tenant."""
|
|
118
|
+
|
|
119
|
+
tenant_id: UUID
|
|
120
|
+
user_id: str
|
|
121
|
+
email: str
|
|
122
|
+
role: str = Field(default="member")
|
|
123
|
+
permissions: list[str] = Field(default_factory=list)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class TenantUserUpdateDTO(BaseModel):
|
|
127
|
+
"""DTO for updating tenant user fields."""
|
|
128
|
+
|
|
129
|
+
role: str | None = None
|
|
130
|
+
permissions: list[str] | None = None
|
|
131
|
+
is_active: bool | None = None
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
"""RFC 9457 Problem Details model (shared)."""
|
|
2
|
-
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ProblemDetail(BaseModel):
|
|
9
|
-
"""RFC 9457 Problem Details model."""
|
|
10
|
-
|
|
11
|
-
type: str = Field(default="about:blank")
|
|
12
|
-
title: str = Field()
|
|
13
|
-
status: int = Field()
|
|
14
|
-
detail: str | None = Field(default=None)
|
|
15
|
-
instance: str | None = Field(default=None)
|
|
16
|
-
|
|
17
|
-
# Extension members
|
|
18
|
-
timestamp: str | None = Field(default=None)
|
|
19
|
-
request_id: str | None = Field(default=None)
|
|
20
|
-
errors: dict[str, Any] | None = Field(default=None)
|
|
21
|
-
|
|
22
|
-
model_config = {"extra": "allow"}
|
|
1
|
+
"""RFC 9457 Problem Details model (shared)."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ProblemDetail(BaseModel):
|
|
9
|
+
"""RFC 9457 Problem Details model."""
|
|
10
|
+
|
|
11
|
+
type: str = Field(default="about:blank")
|
|
12
|
+
title: str = Field()
|
|
13
|
+
status: int = Field()
|
|
14
|
+
detail: str | None = Field(default=None)
|
|
15
|
+
instance: str | None = Field(default=None)
|
|
16
|
+
|
|
17
|
+
# Extension members
|
|
18
|
+
timestamp: str | None = Field(default=None)
|
|
19
|
+
request_id: str | None = Field(default=None)
|
|
20
|
+
errors: dict[str, Any] | None = Field(default=None)
|
|
21
|
+
|
|
22
|
+
model_config = {"extra": "allow"}
|
|
@@ -1,161 +1,161 @@
|
|
|
1
|
-
"""Application settings schemas using Pydantic Settings v2."""
|
|
2
|
-
|
|
3
|
-
from typing import Literal
|
|
4
|
-
|
|
5
|
-
from pydantic import Field, field_validator
|
|
6
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class DatabaseSettings(BaseSettings):
|
|
10
|
-
"""Database configuration settings for the Manager service."""
|
|
11
|
-
|
|
12
|
-
url: str = Field(
|
|
13
|
-
default="postgresql+asyncpg://postgres:postgres@localhost:55432/idun_agents"
|
|
14
|
-
)
|
|
15
|
-
echo: bool = Field(default=False)
|
|
16
|
-
pool_size: int = Field(default=10)
|
|
17
|
-
max_overflow: int = Field(default=20)
|
|
18
|
-
pool_pre_ping: bool = Field(default=True)
|
|
19
|
-
|
|
20
|
-
model_config = SettingsConfigDict(env_prefix="DATABASE_", env_file=".env")
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class RedisSettings(BaseSettings):
|
|
24
|
-
"""Redis cache settings."""
|
|
25
|
-
|
|
26
|
-
url: str = Field(default="redis://localhost:6379/0")
|
|
27
|
-
max_connections: int = Field(default=20)
|
|
28
|
-
|
|
29
|
-
model_config = SettingsConfigDict(env_prefix="REDIS_", env_file=".env")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class AuthSettings(BaseSettings):
|
|
33
|
-
"""Authentication and OIDC-related configuration."""
|
|
34
|
-
|
|
35
|
-
provider_type: Literal["okta", "auth0", "entra", "google"] = Field(default="auth0")
|
|
36
|
-
issuer: str = Field(default="")
|
|
37
|
-
client_id: str = Field(default="")
|
|
38
|
-
client_secret: str = Field(default="")
|
|
39
|
-
audience: str | None = Field(default=None)
|
|
40
|
-
redirect_uri: str | None = Field(default=None)
|
|
41
|
-
scopes: list[str] = Field(default_factory=lambda: ["openid", "profile", "email"])
|
|
42
|
-
|
|
43
|
-
allowed_algs: list[str] = Field(default_factory=lambda: ["RS256", "RS512", "ES256"])
|
|
44
|
-
jwks_cache_ttl: int = Field(default=300)
|
|
45
|
-
clock_skew_seconds: int = Field(default=60)
|
|
46
|
-
expected_audiences: list[str] = Field(default_factory=list)
|
|
47
|
-
|
|
48
|
-
claim_user_id_path: list[str] | None = Field(default=None)
|
|
49
|
-
claim_email_path: list[str] | None = Field(default=None)
|
|
50
|
-
claim_roles_paths: list[list[str]] | None = Field(default=None)
|
|
51
|
-
claim_groups_paths: list[list[str]] | None = Field(default=None)
|
|
52
|
-
claim_workspace_ids_paths: list[list[str]] | None = Field(default=None)
|
|
53
|
-
|
|
54
|
-
@field_validator("claim_user_id_path", "claim_email_path", mode="before")
|
|
55
|
-
@classmethod
|
|
56
|
-
def _string_to_list(cls, v):
|
|
57
|
-
if isinstance(v, str) and v.strip().startswith("#"):
|
|
58
|
-
return None
|
|
59
|
-
return v
|
|
60
|
-
|
|
61
|
-
@field_validator(
|
|
62
|
-
"claim_roles_paths",
|
|
63
|
-
"claim_groups_paths",
|
|
64
|
-
"claim_workspace_ids_paths",
|
|
65
|
-
mode="before",
|
|
66
|
-
)
|
|
67
|
-
@classmethod
|
|
68
|
-
def _string_to_list_of_lists(cls, v):
|
|
69
|
-
if isinstance(v, str) and v.strip().startswith("#"):
|
|
70
|
-
return None
|
|
71
|
-
return v
|
|
72
|
-
|
|
73
|
-
model_config = SettingsConfigDict(
|
|
74
|
-
env_prefix="AUTH_", env_file=".env", extra="ignore"
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
@field_validator("issuer")
|
|
78
|
-
@classmethod
|
|
79
|
-
def validate_issuer(cls, v: str) -> str:
|
|
80
|
-
"""Validate that issuer is either empty or a URL."""
|
|
81
|
-
if not v:
|
|
82
|
-
return v
|
|
83
|
-
if not v.startswith("http"):
|
|
84
|
-
raise ValueError("issuer must be a URL")
|
|
85
|
-
return v
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
class ObservabilitySettings(BaseSettings):
|
|
89
|
-
"""OpenTelemetry and logging settings."""
|
|
90
|
-
|
|
91
|
-
otel_service_name: str = Field(default="idun-agent-manager")
|
|
92
|
-
otel_exporter_endpoint: str | None = Field(default=None)
|
|
93
|
-
otel_exporter_headers: str | None = Field(default=None)
|
|
94
|
-
log_level: str = Field(default="INFO")
|
|
95
|
-
log_format: str = Field(default="json")
|
|
96
|
-
|
|
97
|
-
model_config = SettingsConfigDict(env_prefix="OTEL_", env_file=".env")
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
class CelerySettings(BaseSettings):
|
|
101
|
-
"""Celery broker/result backend settings."""
|
|
102
|
-
|
|
103
|
-
broker_url: str = Field(default="redis://localhost:6379/1")
|
|
104
|
-
result_backend: str = Field(default="redis://localhost:6379/2")
|
|
105
|
-
task_serializer: str = Field(default="json")
|
|
106
|
-
result_serializer: str = Field(default="json")
|
|
107
|
-
accept_content: list[str] = Field(default=["json"])
|
|
108
|
-
timezone: str = Field(default="UTC")
|
|
109
|
-
|
|
110
|
-
model_config = SettingsConfigDict(env_prefix="CELERY_", env_file=".env")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
class APISettings(BaseSettings):
|
|
114
|
-
"""API metadata, CORS, and rate limiting settings."""
|
|
115
|
-
|
|
116
|
-
title: str = Field(default="Idun Agent Manager API")
|
|
117
|
-
description: str = Field(default="Modern FastAPI backend for managing AI agents")
|
|
118
|
-
version: str = Field(default="0.1.0")
|
|
119
|
-
docs_url: str = Field(default="/docs")
|
|
120
|
-
redoc_url: str = Field(default="/redoc")
|
|
121
|
-
openapi_url: str = Field(default="/openapi.json")
|
|
122
|
-
cors_origins: list[str] = Field(
|
|
123
|
-
default=["http://localhost:3000", "http://localhost:8080"]
|
|
124
|
-
)
|
|
125
|
-
cors_methods: list[str] = Field(default=["*"])
|
|
126
|
-
cors_headers: list[str] = Field(default=["*"])
|
|
127
|
-
rate_limit_enabled: bool = Field(default=True)
|
|
128
|
-
rate_limit_requests: int = Field(default=100)
|
|
129
|
-
rate_limit_window: int = Field(default=60)
|
|
130
|
-
|
|
131
|
-
model_config = SettingsConfigDict(
|
|
132
|
-
env_prefix="API_", env_file=".env", extra="ignore"
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
class Settings(BaseSettings):
|
|
137
|
-
"""Top-level application settings composed of sub-settings."""
|
|
138
|
-
|
|
139
|
-
environment: str = Field(default="development")
|
|
140
|
-
debug: bool = Field(default=False)
|
|
141
|
-
testing: bool = Field(default=False)
|
|
142
|
-
host: str = Field(default="0.0.0.0")
|
|
143
|
-
port: int = Field(default=8000)
|
|
144
|
-
workers: int = Field(default=1)
|
|
145
|
-
reload: bool = Field(default=False)
|
|
146
|
-
is_development: bool = Field(default=True)
|
|
147
|
-
|
|
148
|
-
database: DatabaseSettings = Field(default_factory=DatabaseSettings)
|
|
149
|
-
redis: RedisSettings = Field(default_factory=RedisSettings)
|
|
150
|
-
auth: AuthSettings = Field(default_factory=AuthSettings)
|
|
151
|
-
observability: ObservabilitySettings = Field(default_factory=ObservabilitySettings)
|
|
152
|
-
celery: CelerySettings = Field(default_factory=CelerySettings)
|
|
153
|
-
api: APISettings = Field(default_factory=APISettings)
|
|
154
|
-
|
|
155
|
-
model_config = SettingsConfigDict(
|
|
156
|
-
env_file=".env",
|
|
157
|
-
env_file_encoding="utf-8",
|
|
158
|
-
env_nested_delimiter="__",
|
|
159
|
-
case_sensitive=False,
|
|
160
|
-
extra="ignore",
|
|
161
|
-
)
|
|
1
|
+
"""Application settings schemas using Pydantic Settings v2."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from pydantic import Field, field_validator
|
|
6
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DatabaseSettings(BaseSettings):
|
|
10
|
+
"""Database configuration settings for the Manager service."""
|
|
11
|
+
|
|
12
|
+
url: str = Field(
|
|
13
|
+
default="postgresql+asyncpg://postgres:postgres@localhost:55432/idun_agents"
|
|
14
|
+
)
|
|
15
|
+
echo: bool = Field(default=False)
|
|
16
|
+
pool_size: int = Field(default=10)
|
|
17
|
+
max_overflow: int = Field(default=20)
|
|
18
|
+
pool_pre_ping: bool = Field(default=True)
|
|
19
|
+
|
|
20
|
+
model_config = SettingsConfigDict(env_prefix="DATABASE_", env_file=".env")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RedisSettings(BaseSettings):
|
|
24
|
+
"""Redis cache settings."""
|
|
25
|
+
|
|
26
|
+
url: str = Field(default="redis://localhost:6379/0")
|
|
27
|
+
max_connections: int = Field(default=20)
|
|
28
|
+
|
|
29
|
+
model_config = SettingsConfigDict(env_prefix="REDIS_", env_file=".env")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AuthSettings(BaseSettings):
|
|
33
|
+
"""Authentication and OIDC-related configuration."""
|
|
34
|
+
|
|
35
|
+
provider_type: Literal["okta", "auth0", "entra", "google"] = Field(default="auth0")
|
|
36
|
+
issuer: str = Field(default="")
|
|
37
|
+
client_id: str = Field(default="")
|
|
38
|
+
client_secret: str = Field(default="")
|
|
39
|
+
audience: str | None = Field(default=None)
|
|
40
|
+
redirect_uri: str | None = Field(default=None)
|
|
41
|
+
scopes: list[str] = Field(default_factory=lambda: ["openid", "profile", "email"])
|
|
42
|
+
|
|
43
|
+
allowed_algs: list[str] = Field(default_factory=lambda: ["RS256", "RS512", "ES256"])
|
|
44
|
+
jwks_cache_ttl: int = Field(default=300)
|
|
45
|
+
clock_skew_seconds: int = Field(default=60)
|
|
46
|
+
expected_audiences: list[str] = Field(default_factory=list)
|
|
47
|
+
|
|
48
|
+
claim_user_id_path: list[str] | None = Field(default=None)
|
|
49
|
+
claim_email_path: list[str] | None = Field(default=None)
|
|
50
|
+
claim_roles_paths: list[list[str]] | None = Field(default=None)
|
|
51
|
+
claim_groups_paths: list[list[str]] | None = Field(default=None)
|
|
52
|
+
claim_workspace_ids_paths: list[list[str]] | None = Field(default=None)
|
|
53
|
+
|
|
54
|
+
@field_validator("claim_user_id_path", "claim_email_path", mode="before")
|
|
55
|
+
@classmethod
|
|
56
|
+
def _string_to_list(cls, v):
|
|
57
|
+
if isinstance(v, str) and v.strip().startswith("#"):
|
|
58
|
+
return None
|
|
59
|
+
return v
|
|
60
|
+
|
|
61
|
+
@field_validator(
|
|
62
|
+
"claim_roles_paths",
|
|
63
|
+
"claim_groups_paths",
|
|
64
|
+
"claim_workspace_ids_paths",
|
|
65
|
+
mode="before",
|
|
66
|
+
)
|
|
67
|
+
@classmethod
|
|
68
|
+
def _string_to_list_of_lists(cls, v):
|
|
69
|
+
if isinstance(v, str) and v.strip().startswith("#"):
|
|
70
|
+
return None
|
|
71
|
+
return v
|
|
72
|
+
|
|
73
|
+
model_config = SettingsConfigDict(
|
|
74
|
+
env_prefix="AUTH_", env_file=".env", extra="ignore"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@field_validator("issuer")
|
|
78
|
+
@classmethod
|
|
79
|
+
def validate_issuer(cls, v: str) -> str:
|
|
80
|
+
"""Validate that issuer is either empty or a URL."""
|
|
81
|
+
if not v:
|
|
82
|
+
return v
|
|
83
|
+
if not v.startswith("http"):
|
|
84
|
+
raise ValueError("issuer must be a URL")
|
|
85
|
+
return v
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ObservabilitySettings(BaseSettings):
|
|
89
|
+
"""OpenTelemetry and logging settings."""
|
|
90
|
+
|
|
91
|
+
otel_service_name: str = Field(default="idun-agent-manager")
|
|
92
|
+
otel_exporter_endpoint: str | None = Field(default=None)
|
|
93
|
+
otel_exporter_headers: str | None = Field(default=None)
|
|
94
|
+
log_level: str = Field(default="INFO")
|
|
95
|
+
log_format: str = Field(default="json")
|
|
96
|
+
|
|
97
|
+
model_config = SettingsConfigDict(env_prefix="OTEL_", env_file=".env")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class CelerySettings(BaseSettings):
|
|
101
|
+
"""Celery broker/result backend settings."""
|
|
102
|
+
|
|
103
|
+
broker_url: str = Field(default="redis://localhost:6379/1")
|
|
104
|
+
result_backend: str = Field(default="redis://localhost:6379/2")
|
|
105
|
+
task_serializer: str = Field(default="json")
|
|
106
|
+
result_serializer: str = Field(default="json")
|
|
107
|
+
accept_content: list[str] = Field(default=["json"])
|
|
108
|
+
timezone: str = Field(default="UTC")
|
|
109
|
+
|
|
110
|
+
model_config = SettingsConfigDict(env_prefix="CELERY_", env_file=".env")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class APISettings(BaseSettings):
|
|
114
|
+
"""API metadata, CORS, and rate limiting settings."""
|
|
115
|
+
|
|
116
|
+
title: str = Field(default="Idun Agent Manager API")
|
|
117
|
+
description: str = Field(default="Modern FastAPI backend for managing AI agents")
|
|
118
|
+
version: str = Field(default="0.1.0")
|
|
119
|
+
docs_url: str = Field(default="/docs")
|
|
120
|
+
redoc_url: str = Field(default="/redoc")
|
|
121
|
+
openapi_url: str = Field(default="/openapi.json")
|
|
122
|
+
cors_origins: list[str] = Field(
|
|
123
|
+
default=["http://localhost:3000", "http://localhost:8080"]
|
|
124
|
+
)
|
|
125
|
+
cors_methods: list[str] = Field(default=["*"])
|
|
126
|
+
cors_headers: list[str] = Field(default=["*"])
|
|
127
|
+
rate_limit_enabled: bool = Field(default=True)
|
|
128
|
+
rate_limit_requests: int = Field(default=100)
|
|
129
|
+
rate_limit_window: int = Field(default=60)
|
|
130
|
+
|
|
131
|
+
model_config = SettingsConfigDict(
|
|
132
|
+
env_prefix="API_", env_file=".env", extra="ignore"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class Settings(BaseSettings):
|
|
137
|
+
"""Top-level application settings composed of sub-settings."""
|
|
138
|
+
|
|
139
|
+
environment: str = Field(default="development")
|
|
140
|
+
debug: bool = Field(default=False)
|
|
141
|
+
testing: bool = Field(default=False)
|
|
142
|
+
host: str = Field(default="0.0.0.0")
|
|
143
|
+
port: int = Field(default=8000)
|
|
144
|
+
workers: int = Field(default=1)
|
|
145
|
+
reload: bool = Field(default=False)
|
|
146
|
+
is_development: bool = Field(default=True)
|
|
147
|
+
|
|
148
|
+
database: DatabaseSettings = Field(default_factory=DatabaseSettings)
|
|
149
|
+
redis: RedisSettings = Field(default_factory=RedisSettings)
|
|
150
|
+
auth: AuthSettings = Field(default_factory=AuthSettings)
|
|
151
|
+
observability: ObservabilitySettings = Field(default_factory=ObservabilitySettings)
|
|
152
|
+
celery: CelerySettings = Field(default_factory=CelerySettings)
|
|
153
|
+
api: APISettings = Field(default_factory=APISettings)
|
|
154
|
+
|
|
155
|
+
model_config = SettingsConfigDict(
|
|
156
|
+
env_file=".env",
|
|
157
|
+
env_file_encoding="utf-8",
|
|
158
|
+
env_nested_delimiter="__",
|
|
159
|
+
case_sensitive=False,
|
|
160
|
+
extra="ignore",
|
|
161
|
+
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"""Shared cross-cutting schemas used by both Engine and Manager."""
|
|
2
|
-
|
|
3
|
-
from .observability import ObservabilityConfig
|
|
4
|
-
|
|
5
|
-
__all__ = ["ObservabilityConfig"]
|
|
1
|
+
"""Shared cross-cutting schemas used by both Engine and Manager."""
|
|
2
|
+
|
|
3
|
+
from .observability import ObservabilityConfig
|
|
4
|
+
|
|
5
|
+
__all__ = ["ObservabilityConfig"]
|