code editor: Agent Studio improvement iteration 1 for quote-judge-studio-v1. Goal:
This commit is contained in:
2
a2a.yaml
2
a2a.yaml
@@ -1,5 +1,5 @@
|
||||
name: quote-judge-studio-v1
|
||||
version: 0.1.1
|
||||
version: 0.1.2
|
||||
entrypoint: agent:QuoteJudgeStudioV1
|
||||
expose:
|
||||
public: false
|
||||
|
||||
41
agent.py
41
agent.py
@@ -48,6 +48,7 @@ MAX_QUOTES = 20
|
||||
MAX_VENDOR_LENGTH = 120
|
||||
MAX_UPLOADS = 5
|
||||
MAX_UPLOAD_BYTES = 128_000
|
||||
MAX_UPLOAD_BASE64_CHARS = ((MAX_UPLOAD_BYTES + 2) // 3) * 4
|
||||
ALLOWED_UPLOAD_MEDIA_TYPES = {"text/csv", "text/plain", "application/json"}
|
||||
_DATABASE_NAME = "quote-judge-studio-v1-data"
|
||||
_LOCAL_COMPARISONS: dict[tuple[str, str], dict[str, Any]] = {}
|
||||
@@ -79,20 +80,12 @@ class QuoteWeights(BaseModel):
|
||||
delivery: float = Field(..., ge=0, le=100)
|
||||
warranty: float = Field(..., ge=0, le=100)
|
||||
|
||||
@field_validator("warranty")
|
||||
@classmethod
|
||||
def weights_must_be_positive(cls, value: float, info: Any) -> float:
|
||||
values = info.data
|
||||
total = float(values.get("price", 0)) + float(values.get("delivery", 0)) + float(value)
|
||||
if total <= 0:
|
||||
raise ValueError("at least one weight must be positive")
|
||||
return value
|
||||
|
||||
|
||||
class BrowserQuoteDocument(BaseModel):
|
||||
filename: str = Field(..., min_length=1, max_length=160)
|
||||
media_type: str = Field(..., min_length=1, max_length=100)
|
||||
data_base64: str = Field(..., min_length=1)
|
||||
data_base64: str = Field(..., min_length=1, max_length=MAX_UPLOAD_BASE64_CHARS)
|
||||
|
||||
@field_validator("filename")
|
||||
@classmethod
|
||||
@@ -115,7 +108,7 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
"QuoteJudge compares vendor quotes with weighted price, delivery, and "
|
||||
"warranty scoring, persists per-user comparisons, and serves a one-page product UI."
|
||||
)
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
|
||||
config_model = QuoteJudgeStudioV1Config
|
||||
auth_model = PlatformUserAuth
|
||||
@@ -152,7 +145,7 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
"weights; persist the recommendation and a production receipt."
|
||||
),
|
||||
timeout_seconds=30,
|
||||
idempotent=True,
|
||||
idempotent=False,
|
||||
cost_class="cheap",
|
||||
)
|
||||
async def compare_quotes(
|
||||
@@ -171,6 +164,24 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
"weights": weights.model_dump(),
|
||||
}
|
||||
|
||||
if weights.price + weights.delivery + weights.warranty <= 0:
|
||||
result = {
|
||||
"ok": False,
|
||||
"code": "positive_weight_required",
|
||||
"message": "Set at least one weight above zero before comparing.",
|
||||
"comparison_id": cleaned_id,
|
||||
}
|
||||
await _persist_receipt(
|
||||
tenant_key=tenant,
|
||||
comparison_id=cleaned_id,
|
||||
skill_name="compare_quotes",
|
||||
input_payload=input_payload,
|
||||
result_payload=result,
|
||||
status="validation_error",
|
||||
started_at=started,
|
||||
)
|
||||
return result
|
||||
|
||||
if len(quotes) < 2:
|
||||
result = {
|
||||
"ok": False,
|
||||
@@ -222,7 +233,7 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
@a2a.tool(
|
||||
description="Reopen a saved QuoteJudge comparison for the authenticated user.",
|
||||
timeout_seconds=15,
|
||||
idempotent=True,
|
||||
idempotent=False,
|
||||
cost_class="cheap",
|
||||
)
|
||||
async def get_comparison(
|
||||
@@ -261,7 +272,7 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
"compare extracted quotes, and persist the result."
|
||||
),
|
||||
timeout_seconds=30,
|
||||
idempotent=True,
|
||||
idempotent=False,
|
||||
cost_class="cheap",
|
||||
)
|
||||
async def compare_uploaded_quotes_browser(
|
||||
@@ -287,7 +298,7 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
"file and compare extracted quotes."
|
||||
),
|
||||
timeout_seconds=30,
|
||||
idempotent=True,
|
||||
idempotent=False,
|
||||
cost_class="cheap",
|
||||
)
|
||||
async def compare_uploaded_quote_file(
|
||||
@@ -342,6 +353,8 @@ class QuoteJudgeStudioV1(A2AAgent[QuoteJudgeStudioV1Config, PlatformUserAuth]):
|
||||
def _tenant_key(ctx: RunContext[PlatformUserAuth]) -> str:
|
||||
auth = ctx.auth
|
||||
ident = auth.user_id if auth.user_id is not None else (auth.sub or auth.email)
|
||||
if ident is None:
|
||||
raise ValueError("authenticated user identity is required")
|
||||
return f"user:{ident}"
|
||||
|
||||
|
||||
|
||||
10
db/migrations/002_quotejudge_schema_repair.sql
Normal file
10
db/migrations/002_quotejudge_schema_repair.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
ALTER TABLE quote_comparisons
|
||||
ADD COLUMN IF NOT EXISTS recommended_vendor TEXT,
|
||||
ADD COLUMN IF NOT EXISTS score DOUBLE PRECISION,
|
||||
ADD COLUMN IF NOT EXISTS normalized_total DOUBLE PRECISION;
|
||||
|
||||
ALTER TABLE quote_execution_receipts
|
||||
ADD COLUMN IF NOT EXISTS skill_name TEXT,
|
||||
ADD COLUMN IF NOT EXISTS status TEXT,
|
||||
ADD COLUMN IF NOT EXISTS input_hash TEXT,
|
||||
ADD COLUMN IF NOT EXISTS result_hash TEXT;
|
||||
Reference in New Issue
Block a user