examples_api.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. """
  2. Examples API - 提供 examples 项目列表和 prompt 读取接口
  3. """
  4. import os
  5. from typing import List, Optional
  6. from pathlib import Path
  7. from fastapi import APIRouter, HTTPException
  8. from pydantic import BaseModel
  9. router = APIRouter(prefix="/api/examples", tags=["examples"])
  10. class ExampleProject(BaseModel):
  11. """Example 项目信息"""
  12. name: str
  13. path: str
  14. has_prompt: bool
  15. class ExampleListResponse(BaseModel):
  16. """Example 列表响应"""
  17. projects: List[ExampleProject]
  18. class PromptResponse(BaseModel):
  19. """Prompt 响应"""
  20. system_prompt: str
  21. user_prompt: str
  22. model: Optional[str] = None
  23. temperature: Optional[float] = None
  24. # 配置 examples 目录路径
  25. EXAMPLES_DIR = Path("examples")
  26. @router.get("", response_model=ExampleListResponse)
  27. async def list_examples():
  28. """
  29. 列出所有 example 项目
  30. 扫描 examples 目录,返回所有子目录及其 prompt 文件状态
  31. """
  32. if not EXAMPLES_DIR.exists():
  33. return ExampleListResponse(projects=[])
  34. projects = []
  35. for item in EXAMPLES_DIR.iterdir():
  36. if item.is_dir():
  37. # 检查是否有 prompt 文件
  38. prompt_file = item / "production.prompt"
  39. has_prompt = prompt_file.exists()
  40. projects.append(ExampleProject(
  41. name=item.name,
  42. path=str(item),
  43. has_prompt=has_prompt
  44. ))
  45. # 按名称排序
  46. projects.sort(key=lambda x: x.name)
  47. return ExampleListResponse(projects=projects)
  48. @router.get("/{project_name}/prompt", response_model=PromptResponse)
  49. async def get_example_prompt(project_name: str):
  50. """
  51. 获取指定 example 项目的 prompt
  52. 读取 production.prompt 文件,解析 frontmatter 和内容
  53. """
  54. project_path = EXAMPLES_DIR / project_name
  55. if not project_path.exists() or not project_path.is_dir():
  56. raise HTTPException(status_code=404, detail=f"Project not found: {project_name}")
  57. prompt_file = project_path / "production.prompt"
  58. if not prompt_file.exists():
  59. raise HTTPException(status_code=404, detail=f"Prompt file not found for project: {project_name}")
  60. try:
  61. content = prompt_file.read_text(encoding="utf-8")
  62. # 解析 frontmatter 和内容
  63. system_prompt = ""
  64. user_prompt = ""
  65. model = None
  66. temperature = None
  67. # 检查是否有 frontmatter
  68. if content.startswith("---"):
  69. parts = content.split("---", 2)
  70. if len(parts) >= 3:
  71. frontmatter = parts[1].strip()
  72. body = parts[2].strip()
  73. # 解析 frontmatter
  74. for line in frontmatter.split("\n"):
  75. if ":" in line:
  76. key, value = line.split(":", 1)
  77. key = key.strip()
  78. value = value.strip()
  79. if key == "model":
  80. model = value
  81. elif key == "temperature":
  82. try:
  83. temperature = float(value)
  84. except ValueError:
  85. pass
  86. else:
  87. body = content
  88. else:
  89. body = content
  90. # 解析 $system$ 和 $user$ 部分
  91. if "$system$" in body:
  92. parts = body.split("$system$", 1)
  93. if len(parts) > 1:
  94. rest = parts[1]
  95. if "$user$" in rest:
  96. system_part, user_part = rest.split("$user$", 1)
  97. system_prompt = system_part.strip()
  98. user_prompt = user_part.strip()
  99. else:
  100. system_prompt = rest.strip()
  101. elif "$user$" in body:
  102. parts = body.split("$user$", 1)
  103. if len(parts) > 1:
  104. user_prompt = parts[1].strip()
  105. else:
  106. # 没有标记,全部作为 user_prompt
  107. user_prompt = body.strip()
  108. return PromptResponse(
  109. system_prompt=system_prompt,
  110. user_prompt=user_prompt,
  111. model=model,
  112. temperature=temperature
  113. )
  114. except Exception as e:
  115. raise HTTPException(status_code=500, detail=f"Failed to read prompt file: {str(e)}")