deploy
This commit is contained in:
81
agent.py
81
agent.py
@@ -5,6 +5,7 @@ import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
import httpx
|
||||
from a2a_pack.deepagents import create_a2a_deep_agent
|
||||
@@ -53,8 +54,11 @@ LEARNHOUSE_CONTEXT = (
|
||||
"create at least one activity in each chapter using that chapter's specific "
|
||||
"chapter_id. Do not attach all activities to the first chapter. For visible "
|
||||
"lesson content, create activities with activity_type TYPE_DYNAMIC, "
|
||||
"activity_sub_type SUBTYPE_DYNAMIC_MARKDOWN, markdown content/details, "
|
||||
"lock_type public, and published=true unless the user requests drafts. "
|
||||
"activity_sub_type SUBTYPE_DYNAMIC_MARKDOWN, content.markdown_url pointing "
|
||||
"to fetchable markdown, lock_type public, and published=true unless the "
|
||||
"user requests drafts. The renderer ignores inline content.body and "
|
||||
"content.markdown unless content.markdown_url is also configured; when no "
|
||||
"external markdown URL is available, use a data:text/markdown URL. "
|
||||
"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 "
|
||||
@@ -77,7 +81,7 @@ class OperationInput(BaseModel):
|
||||
class LearnhouseAgent(A2AAgent):
|
||||
name = "learnhouse-agent"
|
||||
description = "LearnHouse is an open-source platform tailored for learning experiences."
|
||||
version = "1.2.12"
|
||||
version = "1.2.13"
|
||||
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(
|
||||
@@ -309,8 +313,10 @@ class LearnhouseAgent(A2AAgent):
|
||||
parts.append(
|
||||
"For LearnHouse activity creation, send JSON fields name, "
|
||||
"chapter_id, activity_type, activity_sub_type, content, "
|
||||
"details, lock_type, and published. Use the target "
|
||||
"chapter's own chapter_id; do not reuse the first chapter ID."
|
||||
"details, lock_type, and published. Markdown activities "
|
||||
"must include content.markdown_url. If only inline markdown "
|
||||
"is available, this agent converts it to a data:text/markdown URL. "
|
||||
"Use the target chapter's own chapter_id; do not reuse the first chapter ID."
|
||||
)
|
||||
if _is_learnhouse_update_activity(operation):
|
||||
parts.append(
|
||||
@@ -348,6 +354,7 @@ class LearnhouseAgent(A2AAgent):
|
||||
body = json.loads(stripped)
|
||||
except ValueError:
|
||||
pass
|
||||
body = _normalize_learnhouse_activity_body(operation, body)
|
||||
if _operation_uses_multipart(operation):
|
||||
request_kwargs["data"] = body
|
||||
else:
|
||||
@@ -540,6 +547,70 @@ def _is_learnhouse_update_activity(operation: dict[str, Any]) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _normalize_learnhouse_activity_body(operation: dict[str, Any], body: Any) -> Any:
|
||||
if not (
|
||||
_is_learnhouse_create_activity(operation)
|
||||
or _is_learnhouse_update_activity(operation)
|
||||
):
|
||||
return body
|
||||
if not isinstance(body, dict):
|
||||
return body
|
||||
|
||||
normalized = dict(body)
|
||||
if _is_learnhouse_update_activity(operation):
|
||||
normalized.pop("published_version", None)
|
||||
|
||||
content = normalized.get("content")
|
||||
if not isinstance(content, dict):
|
||||
content = {}
|
||||
else:
|
||||
content = dict(content)
|
||||
|
||||
details = normalized.get("details")
|
||||
details_dict = details if isinstance(details, dict) else {}
|
||||
metadata = normalized.get("extra_metadata")
|
||||
metadata_dict = metadata if isinstance(metadata, dict) else {}
|
||||
|
||||
sub_type = str(normalized.get("activity_sub_type") or content.get("activity_sub_type") or "")
|
||||
has_markdown_hint = (
|
||||
sub_type == "SUBTYPE_DYNAMIC_MARKDOWN"
|
||||
or any(key in content for key in ("markdown_url", "markdown", "body", "text"))
|
||||
or any(key in details_dict for key in ("markdown", "body", "text"))
|
||||
or any(key in metadata_dict for key in ("markdown", "body", "text"))
|
||||
)
|
||||
if not has_markdown_hint:
|
||||
return normalized
|
||||
|
||||
normalized.setdefault("activity_type", "TYPE_DYNAMIC")
|
||||
normalized["activity_sub_type"] = "SUBTYPE_DYNAMIC_MARKDOWN"
|
||||
markdown_url = str(content.get("markdown_url") or "").strip()
|
||||
if not markdown_url:
|
||||
markdown_text = _extract_markdown_text(content, details_dict, metadata_dict, normalized)
|
||||
if markdown_text:
|
||||
content["markdown_url"] = _markdown_data_url(markdown_text)
|
||||
normalized["content"] = content
|
||||
|
||||
if isinstance(details, dict):
|
||||
normalized["details"] = dict(details, content_type=details.get("content_type") or "markdown")
|
||||
elif details is None:
|
||||
normalized["details"] = {"content_type": "markdown"}
|
||||
return normalized
|
||||
|
||||
|
||||
def _extract_markdown_text(*sources: dict[str, Any]) -> str:
|
||||
for source in sources:
|
||||
for key in ("markdown", "body", "text", "content"):
|
||||
value = source.get(key)
|
||||
if isinstance(value, str) and value.strip():
|
||||
return value.strip()
|
||||
title = str(sources[-1].get("name") or "Lesson").strip()
|
||||
return f"# {title}\n"
|
||||
|
||||
|
||||
def _markdown_data_url(markdown_text: str) -> str:
|
||||
return "data:text/markdown;charset=utf-8," + quote(markdown_text, safe="")
|
||||
|
||||
|
||||
def _apply_learnhouse_defaults(
|
||||
operation: dict[str, Any],
|
||||
parameters: dict[str, Any],
|
||||
|
||||
Reference in New Issue
Block a user