deploy
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "learnhouse-agent",
|
"name": "learnhouse-agent",
|
||||||
"description": "LearnHouse is an open-source platform tailored for learning experiences.",
|
"description": "LearnHouse is an open-source platform tailored for learning experiences.",
|
||||||
"version": "1.2.11",
|
"version": "1.2.12",
|
||||||
"entrypoint": {
|
"entrypoint": {
|
||||||
"module": "agent",
|
"module": "agent",
|
||||||
"class_name": "LearnhouseAgent",
|
"class_name": "LearnhouseAgent",
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
"source": "python-a2a-pack",
|
"source": "python-a2a-pack",
|
||||||
"project_manifest": {
|
"project_manifest": {
|
||||||
"name": "learnhouse-agent",
|
"name": "learnhouse-agent",
|
||||||
"version": "1.2.11",
|
"version": "1.2.12",
|
||||||
"entrypoint": "agent:LearnhouseAgent"
|
"entrypoint": "agent:LearnhouseAgent"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ Deployment context:
|
|||||||
`published=true` unless the user explicitly asks for drafts.
|
`published=true` unless the user explicitly asks for drafts.
|
||||||
- Activity updates must not send `published_version`; this LearnHouse backend
|
- Activity updates must not send `published_version`; this LearnHouse backend
|
||||||
currently rejects that field.
|
currently rejects that field.
|
||||||
|
- Do not use `GET /api/v1/health` as a preflight or blocker for API-token
|
||||||
|
workflows; it returns 403 for API tokens. Smoke/status checks should use a
|
||||||
|
read-only course count/list endpoint with `org_slug=default`.
|
||||||
- Auth setup is required as `LEARNHOUSE_AUTH`. Use `Bearer <access_token>`, a
|
- Auth setup is required as `LEARNHOUSE_AUTH`. Use `Bearer <access_token>`, a
|
||||||
raw access token, or a Cookie header containing `LH_access`/`LH_refresh`.
|
raw access token, or a Cookie header containing `LH_access`/`LH_refresh`.
|
||||||
|
|
||||||
|
|||||||
2
a2a.yaml
2
a2a.yaml
@@ -1,5 +1,5 @@
|
|||||||
name: learnhouse-agent
|
name: learnhouse-agent
|
||||||
version: 1.2.11
|
version: 1.2.12
|
||||||
entrypoint: agent:LearnhouseAgent
|
entrypoint: agent:LearnhouseAgent
|
||||||
description: LearnHouse is an open-source platform tailored for learning experiences.
|
description: LearnHouse is an open-source platform tailored for learning experiences.
|
||||||
runtime:
|
runtime:
|
||||||
|
|||||||
41
agent.py
41
agent.py
@@ -58,7 +58,11 @@ LEARNHOUSE_CONTEXT = (
|
|||||||
"After writing course content, verify every chapter has at least one "
|
"After writing course content, verify every chapter has at least one "
|
||||||
"activity through course metadata or the chapter activity endpoint. When "
|
"activity through course metadata or the chapter activity endpoint. When "
|
||||||
"updating activities, do not send published_version; this LearnHouse "
|
"updating activities, do not send published_version; this LearnHouse "
|
||||||
"backend currently rejects that field."
|
"backend currently rejects that field. Do not use GET /api/v1/health as "
|
||||||
|
"a preflight or blocker for API-token workflows; that endpoint rejects API "
|
||||||
|
"tokens with 403. For smoke/status checks, use the read-only courses count "
|
||||||
|
"or course list endpoint with org_slug 'default', then proceed to the "
|
||||||
|
"requested courses, chapters, or activities endpoint."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -73,7 +77,7 @@ class OperationInput(BaseModel):
|
|||||||
class LearnhouseAgent(A2AAgent):
|
class LearnhouseAgent(A2AAgent):
|
||||||
name = "learnhouse-agent"
|
name = "learnhouse-agent"
|
||||||
description = "LearnHouse is an open-source platform tailored for learning experiences."
|
description = "LearnHouse is an open-source platform tailored for learning experiences."
|
||||||
version = "1.2.11"
|
version = "1.2.12"
|
||||||
consumer_setup = ConsumerSetup.from_fields(
|
consumer_setup = ConsumerSetup.from_fields(
|
||||||
ConsumerSetupField.config("OPENAPI_BASE_URL", label="API base URL", description="Override the default API server (https://learnhouse.a2acloud.io).", required=False, input_type="url"),
|
ConsumerSetupField.config("OPENAPI_BASE_URL", label="API base URL", description="Override the default API server (https://learnhouse.a2acloud.io).", required=False, input_type="url"),
|
||||||
ConsumerSetupField.secret(
|
ConsumerSetupField.secret(
|
||||||
@@ -224,7 +228,7 @@ class LearnhouseAgent(A2AAgent):
|
|||||||
LEARNHOUSE_CONTEXT,
|
LEARNHOUSE_CONTEXT,
|
||||||
"Delegate API work to the matching subagent with the task tool.",
|
"Delegate API work to the matching subagent with the task tool.",
|
||||||
"Do not invent API responses; ask subagents to call their tools for real results.",
|
"Do not invent API responses; ask subagents to call their tools for real results.",
|
||||||
"For smoke tests, health checks, status checks, or ping-style requests, prefer explicit health/status/ping route groups over a generic root '/' route group.",
|
"Do not call GET /api/v1/health as a preflight; it rejects API tokens. For LearnHouse smoke/status checks, use the read-only courses count or courses list endpoint for org_slug 'default'.",
|
||||||
"If a subagent reports missing consumer setup, tell the user which setup field is required.",
|
"If a subagent reports missing consumer setup, tell the user which setup field is required.",
|
||||||
"If a subagent reports that the generated OpenAPI agent may be stale, ask whether the user wants to refresh it from the latest OpenAPI spec.",
|
"If a subagent reports that the generated OpenAPI agent may be stale, ask whether the user wants to refresh it from the latest OpenAPI spec.",
|
||||||
"",
|
"",
|
||||||
@@ -239,7 +243,7 @@ class LearnhouseAgent(A2AAgent):
|
|||||||
"You operate an API through generated OpenAPI tools.",
|
"You operate an API through generated OpenAPI tools.",
|
||||||
LEARNHOUSE_CONTEXT,
|
LEARNHOUSE_CONTEXT,
|
||||||
"Call tools to get real results. Do not invent API responses.",
|
"Call tools to get real results. Do not invent API responses.",
|
||||||
"For smoke tests, health checks, status checks, or ping-style requests, prefer explicit health/status/ping GET operations over generic root '/' operations.",
|
"Do not call GET /api/v1/health as a preflight; it rejects API tokens. For LearnHouse smoke/status checks, use the read-only courses count or courses list endpoint for org_slug 'default'.",
|
||||||
"Use a root '/' operation only when the user asks for the service root/homepage or no more specific read-only operation fits.",
|
"Use a root '/' operation only when the user asks for the service root/homepage or no more specific read-only operation fits.",
|
||||||
"For write, update, or delete operations, explain the intended action before calling the tool.",
|
"For write, update, or delete operations, explain the intended action before calling the tool.",
|
||||||
"If an operation reports missing consumer setup, tell the user which setup field is required.",
|
"If an operation reports missing consumer setup, tell the user which setup field is required.",
|
||||||
@@ -260,7 +264,7 @@ class LearnhouseAgent(A2AAgent):
|
|||||||
LEARNHOUSE_CONTEXT,
|
LEARNHOUSE_CONTEXT,
|
||||||
"Use the available operation tools to make real API calls. Do not invent API responses.",
|
"Use the available operation tools to make real API calls. Do not invent API responses.",
|
||||||
"Read the route group's SKILL.md when it applies; it lists every route you cover.",
|
"Read the route group's SKILL.md when it applies; it lists every route you cover.",
|
||||||
"For smoke tests, health checks, status checks, or ping-style requests, prefer explicit health/status/ping GET operations over generic root '/' operations.",
|
"Do not call GET /api/v1/health as a preflight; it rejects API tokens. For LearnHouse smoke/status checks, use the read-only courses count or courses list endpoint for org_slug 'default'.",
|
||||||
"Use a root '/' operation only when the user asks for the service root/homepage or no more specific read-only operation fits.",
|
"Use a root '/' operation only when the user asks for the service root/homepage or no more specific read-only operation fits.",
|
||||||
"For write, update, or delete operations, explain the intended action before calling the tool.",
|
"For write, update, or delete operations, explain the intended action before calling the tool.",
|
||||||
"If an operation reports missing consumer setup, return the exact setup field name.",
|
"If an operation reports missing consumer setup, return the exact setup field name.",
|
||||||
@@ -571,16 +575,33 @@ def _select_smoke_operation(goal: str) -> str | None:
|
|||||||
smoke_terms = ("smoke", "health", "status", "ping", "readiness", "liveness")
|
smoke_terms = ("smoke", "health", "status", "ping", "readiness", "liveness")
|
||||||
if not any(term in text for term in smoke_terms):
|
if not any(term in text for term in smoke_terms):
|
||||||
return None
|
return None
|
||||||
preferred_terms = ("health", "status", "ping", "ready", "readiness", "live", "liveness")
|
|
||||||
|
# LearnHouse's health endpoint rejects API tokens with 403. Use a harmless
|
||||||
|
# read-only domain endpoint instead so smoke checks do not block real work.
|
||||||
|
preferred_operation_ids = (
|
||||||
|
"api_get_courses_count_api_v1_courses_org_slug_org_slug_count_get",
|
||||||
|
"api_get_course_by_orgslug_api_v1_courses_org_slug_org_slug_page_page_limit_limit",
|
||||||
|
"api_get_org_by_slug_api_v1_orgs_slug_org_slug_get",
|
||||||
|
)
|
||||||
|
for operation_id in preferred_operation_ids:
|
||||||
|
if operation_id in OPERATIONS:
|
||||||
|
return operation_id
|
||||||
|
|
||||||
|
preferred_terms = ("courses", "course", "org", "organization", "status", "ping", "ready", "readiness")
|
||||||
candidates: list[tuple[int, int, str]] = []
|
candidates: list[tuple[int, int, str]] = []
|
||||||
for operation_id, operation in OPERATIONS.items():
|
for operation_id, operation in OPERATIONS.items():
|
||||||
if str(operation.get("method") or "").upper() != "GET":
|
if str(operation.get("method") or "").upper() != "GET":
|
||||||
continue
|
continue
|
||||||
if operation.get("destructive"):
|
if operation.get("destructive"):
|
||||||
continue
|
continue
|
||||||
if any(param.get("required") for param in operation.get("parameters") or []):
|
|
||||||
continue
|
|
||||||
path = str(operation.get("path") or "")
|
path = str(operation.get("path") or "")
|
||||||
|
if _is_learnhouse_health_operation(operation):
|
||||||
|
continue
|
||||||
|
if any(
|
||||||
|
param.get("required") and str(param.get("name")) not in {"org_slug", "org_id", "page", "limit"}
|
||||||
|
for param in operation.get("parameters") or []
|
||||||
|
):
|
||||||
|
continue
|
||||||
searchable = " ".join(
|
searchable = " ".join(
|
||||||
str(operation.get(key) or "")
|
str(operation.get(key) or "")
|
||||||
for key in ("operation_id", "skill_name", "path", "summary", "description")
|
for key in ("operation_id", "skill_name", "path", "summary", "description")
|
||||||
@@ -604,6 +625,10 @@ def _select_smoke_operation(goal: str) -> str | None:
|
|||||||
return min(candidates)[2]
|
return min(candidates)[2]
|
||||||
|
|
||||||
|
|
||||||
|
def _is_learnhouse_health_operation(operation: dict[str, Any]) -> bool:
|
||||||
|
return str(operation.get("path") or "").rstrip("/") == "/api/v1/health"
|
||||||
|
|
||||||
|
|
||||||
def _body_preview(body: Any, limit: int) -> str:
|
def _body_preview(body: Any, limit: int) -> str:
|
||||||
if isinstance(body, str):
|
if isinstance(body, str):
|
||||||
text = body
|
text = body
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ Deployment context for this LearnHouse instance:
|
|||||||
- Course creation must send multipart form fields `name`, `description`, `public`, and `about`. If the user only gives a title, use it as `name`, provide short `description` and `about` values, and set `public=false` unless the user asks for a public course.
|
- Course creation must send multipart form fields `name`, `description`, `public`, and `about`. If the user only gives a title, use it as `name`, provide short `description` and `about` values, and set `public=false` unless the user asks for a public course.
|
||||||
- When the user asks for a course with lessons, modules, or activities, do not stop after creating the course and chapters. Create at least one activity per chapter, using each chapter's own returned `chapter_id`.
|
- When the user asks for a course with lessons, modules, or activities, do not stop after creating the course and chapters. Create at least one activity per chapter, using each chapter's own returned `chapter_id`.
|
||||||
- Do not attach all generated activities to the first chapter. Verify the resulting course metadata shows activities distributed under the intended chapters.
|
- Do not attach all generated activities to the first chapter. Verify the resulting course metadata shows activities distributed under the intended chapters.
|
||||||
|
- For smoke/status checks, use course count or course list with default `org_slug=default`; do not call `GET /api/v1/health` as a preflight.
|
||||||
- Course creation and course management require `LEARNHOUSE_AUTH` consumer setup.
|
- Course creation and course management require `LEARNHOUSE_AUTH` consumer setup.
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ Use the generated operation tools to make real API calls. Do not invent API resp
|
|||||||
If a tool reports missing setup, return the exact setup field name to the caller.
|
If a tool reports missing setup, return the exact setup field name to the caller.
|
||||||
For write, update, or delete operations, state the intended action before calling the tool.
|
For write, update, or delete operations, state the intended action before calling the tool.
|
||||||
|
|
||||||
|
Do not use this endpoint as a preflight for LearnHouse API-token workflows.
|
||||||
|
`GET /api/v1/health` rejects API tokens with 403. For smoke/status checks,
|
||||||
|
use a read-only course count or course list endpoint with `org_slug=default`.
|
||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
### health_api_v1_health_get
|
### health_api_v1_health_get
|
||||||
|
|||||||
Reference in New Issue
Block a user