deploy
This commit is contained in:
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
2
a2a.yaml
2
a2a.yaml
@@ -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:
|
||||
|
||||
41
agent.py
41
agent.py
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user