This commit is contained in:
a2a-platform
2026-06-29 01:20:33 +00:00
parent 5cf3dbe409
commit a519e7b558
6 changed files with 44 additions and 11 deletions

View File

@@ -3,7 +3,7 @@
"language": "python",
"name": "learnhouse-agent",
"description": "LearnHouse is an open-source platform tailored for learning experiences.",
"version": "1.2.11",
"version": "1.2.12",
"entrypoint": {
"module": "agent",
"class_name": "LearnhouseAgent",
@@ -172,7 +172,7 @@
"source": "python-a2a-pack",
"project_manifest": {
"name": "learnhouse-agent",
"version": "1.2.11",
"version": "1.2.12",
"entrypoint": "agent:LearnhouseAgent"
}
}

View File

@@ -25,6 +25,9 @@ Deployment context:
`published=true` unless the user explicitly asks for drafts.
- Activity updates must not send `published_version`; this LearnHouse backend
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
raw access token, or a Cookie header containing `LH_access`/`LH_refresh`.

View File

@@ -1,5 +1,5 @@
name: learnhouse-agent
version: 1.2.11
version: 1.2.12
entrypoint: agent:LearnhouseAgent
description: LearnHouse is an open-source platform tailored for learning experiences.
runtime:

View File

@@ -58,7 +58,11 @@ LEARNHOUSE_CONTEXT = (
"After writing course content, verify every chapter has at least one "
"activity through course metadata or the chapter activity endpoint. When "
"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):
name = "learnhouse-agent"
description = "LearnHouse is an open-source platform tailored for learning experiences."
version = "1.2.11"
version = "1.2.12"
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.secret(
@@ -224,7 +228,7 @@ class LearnhouseAgent(A2AAgent):
LEARNHOUSE_CONTEXT,
"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.",
"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 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.",
LEARNHOUSE_CONTEXT,
"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.",
"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.",
@@ -260,7 +264,7 @@ class LearnhouseAgent(A2AAgent):
LEARNHOUSE_CONTEXT,
"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.",
"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.",
"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.",
@@ -571,16 +575,33 @@ def _select_smoke_operation(goal: str) -> str | None:
smoke_terms = ("smoke", "health", "status", "ping", "readiness", "liveness")
if not any(term in text for term in smoke_terms):
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]] = []
for operation_id, operation in OPERATIONS.items():
if str(operation.get("method") or "").upper() != "GET":
continue
if operation.get("destructive"):
continue
if any(param.get("required") for param in operation.get("parameters") or []):
continue
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(
str(operation.get(key) or "")
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]
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:
if isinstance(body, str):
text = body

View File

@@ -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.
- 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.
- 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.
## Operations

View File

@@ -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.
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
### health_api_v1_health_get