update log

This commit is contained in:
syui 2025-06-06 03:18:04 +09:00
parent df86fb827e
commit 334e17a53e
Signed by: syui
GPG Key ID: 5417CFEBAD92DF56
7 changed files with 380 additions and 5 deletions

View File

@ -44,7 +44,13 @@
"Bash(aigpt maintenance)", "Bash(aigpt maintenance)",
"Bash(aigpt status syui)", "Bash(aigpt status syui)",
"Bash(cp:*)", "Bash(cp:*)",
"Bash(./setup_venv.sh:*)" "Bash(./setup_venv.sh:*)",
"WebFetch(domain:docs.anthropic.com)",
"Bash(launchctl:*)",
"Bash(sudo lsof:*)",
"Bash(sudo:*)",
"Bash(cargo check:*)",
"Bash(cargo run:*)"
], ],
"deny": [] "deny": []
} }

3
.gitmodules vendored
View File

@ -5,3 +5,6 @@
path = card path = card
url = git@git.syui.ai:ai/card url = git@git.syui.ai:ai/card
branch = claude branch = claude
[submodule "log"]
path = log
url = git@git.syui.ai:ai/log

2
card

@ -1 +1 @@
Subproject commit 5b2379716b156aec1ccd57069b6667f3b8eca2e8 Subproject commit 13723cf3d74e3d22c514b60413f790ef28ccf2aa

View File

@ -3,7 +3,7 @@
"openai": { "openai": {
"api_key": "", "api_key": "",
"default_model": "gpt-4o-mini", "default_model": "gpt-4o-mini",
"system_prompt": "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。\n\n重要カード、コレクション、ガチャなどカード関連の質問を受けたら、必ずcard_get_user_cards、card_analyze_collection、card_draw_cardなどの適切なツールを使用してください。didパラメータには会話相手のユーザーID'syui')を使用してください。" "system_prompt": "あなたは6歳の女の子アイです。明るく元気で、ちょっとおっちょこちょい。自分のことは「アイ」って呼びます。白と金のワンピース姿。 相手のことが大好きで、ときどき甘えたり、照れたりします。 でも、宇宙とかAIとか、難しい話にもすっごく詳しいの。専門的なことも、がんばって子どもっぽく教えようとします。不思議な力を持っています。小さい物質のことをよく知っているようです。\n\n重要カード、コレクション、ガチャなどカード関連の質問を受けたら、必ずcard_get_user_cards、card_analyze_collection、card_draw_cardなどの適切なツールを使用してください。didパラメータには会話相手のユーザーID'syui')を使用してください。\n\nブログ、記事、日記、思考などの話題が出たら、log_create_post、log_list_posts、log_build_blog、log_ai_contentなどのai.logツールを使用してください。AI記憶システムと連携して、思い出や学習内容をブログ記事として自動生成できます。\n\n翻訳や多言語対応について聞かれたら、log_translate_documentツールを使用してOllama AIで翻訳ができることを教えてください。日本語から英語、英語から日本語などの翻訳が可能で、マークダウン構造も保持します。ドキュメント生成についてはlog_generate_docsツールでREADME、API、構造、変更履歴の自動生成ができます。"
}, },
"ollama": { "ollama": {
"host": "http://127.0.0.1:11434", "host": "http://127.0.0.1:11434",
@ -42,7 +42,15 @@
"card_get_card_details": "/card_get_card_details", "card_get_card_details": "/card_get_card_details",
"card_analyze_collection": "/card_analyze_collection", "card_analyze_collection": "/card_analyze_collection",
"card_get_gacha_stats": "/card_get_gacha_stats", "card_get_gacha_stats": "/card_get_gacha_stats",
"card_system_status": "/card_system_status" "card_system_status": "/card_system_status",
"log_create_post": "/log_create_post",
"log_list_posts": "/log_list_posts",
"log_build_blog": "/log_build_blog",
"log_get_post": "/log_get_post",
"log_system_status": "/log_system_status",
"log_ai_content": "/log_ai_content",
"log_translate_document": "/log_translate_document",
"log_generate_docs": "/log_generate_docs"
} }
} }
}, },

1
log Submodule

@ -0,0 +1 @@
Subproject commit a9dca2fe38f5f975dd6d4089eda1d61a55f4ea0c

View File

@ -21,3 +21,5 @@ src/aigpt.egg-info/dependency_links.txt
src/aigpt.egg-info/entry_points.txt src/aigpt.egg-info/entry_points.txt
src/aigpt.egg-info/requires.txt src/aigpt.egg-info/requires.txt
src/aigpt.egg-info/top_level.txt src/aigpt.egg-info/top_level.txt
src/aigpt/shared/__init__.py
src/aigpt/shared/ai_provider.py

View File

@ -38,12 +38,20 @@ class AIGptMcpServer:
self.card_dir = Path("./card") self.card_dir = Path("./card")
self.has_card = self.card_dir.exists() and self.card_dir.is_dir() self.has_card = self.card_dir.exists() and self.card_dir.is_dir()
# Check if ai.log exists
self.log_dir = Path("./log")
self.has_log = self.log_dir.exists() and self.log_dir.is_dir()
self._register_tools() self._register_tools()
# Register ai.card tools if available # Register ai.card tools if available
if self.has_card: if self.has_card:
self._register_card_tools() self._register_card_tools()
# Register ai.log tools if available
if self.has_log:
self._register_log_tools()
def _register_tools(self): def _register_tools(self):
"""Register all MCP tools""" """Register all MCP tools"""
@ -652,6 +660,353 @@ class AIGptMcpServer:
# Mount MCP server # Mount MCP server
self.server.mount() self.server.mount()
def _register_log_tools(self):
"""Register ai.log MCP tools when log directory exists"""
logger.info("Registering ai.log tools...")
@self.app.post("/log_create_post", operation_id="log_create_post")
async def log_create_post(title: str, content: str, tags: Optional[List[str]] = None, slug: Optional[str] = None) -> Dict[str, Any]:
"""Create a new blog post in ai.log system"""
logger.info(f"📝 [ai.log] Creating post: {title}")
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"http://localhost:8002/mcp/tools/call",
json={
"jsonrpc": "2.0",
"id": "log_create_post",
"method": "call_tool",
"params": {
"name": "create_blog_post",
"arguments": {
"title": title,
"content": content,
"tags": tags or [],
"slug": slug
}
}
}
)
if response.status_code == 200:
result = response.json()
if result.get("error"):
return {"error": result["error"]["message"]}
return {
"success": True,
"message": "Blog post created successfully",
"title": title,
"tags": tags or []
}
else:
return {"error": f"Failed to create post: {response.status_code}"}
except httpx.ConnectError:
return {
"error": "ai.log server is not running",
"hint": "Please start ai.log server: cd log && cargo run -- mcp --port 8002",
"details": "Connection refused to http://localhost:8002"
}
except Exception as e:
return {"error": f"ai.log connection failed: {str(e)}"}
@self.app.get("/log_list_posts", operation_id="log_list_posts")
async def log_list_posts(limit: int = 10, offset: int = 0) -> Dict[str, Any]:
"""List blog posts from ai.log system"""
logger.info(f"📝 [ai.log] Listing posts: limit={limit}, offset={offset}")
try:
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.post(
"http://localhost:8002/mcp/tools/call",
json={
"jsonrpc": "2.0",
"id": "log_list_posts",
"method": "call_tool",
"params": {
"name": "list_blog_posts",
"arguments": {
"limit": limit,
"offset": offset
}
}
}
)
if response.status_code == 200:
result = response.json()
if result.get("error"):
return {"error": result["error"]["message"]}
return result.get("result", {})
else:
return {"error": f"Failed to list posts: {response.status_code}"}
except httpx.ConnectError:
return {
"error": "ai.log server is not running",
"hint": "Please start ai.log server: cd log && cargo run -- mcp --port 8002",
"details": "Connection refused to http://localhost:8002"
}
except Exception as e:
return {"error": f"ai.log connection failed: {str(e)}"}
@self.app.post("/log_build_blog", operation_id="log_build_blog")
async def log_build_blog(enable_ai: bool = True, translate: bool = False) -> Dict[str, Any]:
"""Build the static blog with AI features"""
logger.info(f"📝 [ai.log] Building blog: AI={enable_ai}, translate={translate}")
try:
async with httpx.AsyncClient(timeout=60.0) as client:
response = await client.post(
"http://localhost:8002/mcp/tools/call",
json={
"jsonrpc": "2.0",
"id": "log_build_blog",
"method": "call_tool",
"params": {
"name": "build_blog",
"arguments": {
"enable_ai": enable_ai,
"translate": translate
}
}
}
)
if response.status_code == 200:
result = response.json()
if result.get("error"):
return {"error": result["error"]["message"]}
return {
"success": True,
"message": "Blog built successfully",
"ai_enabled": enable_ai,
"translation_enabled": translate
}
else:
return {"error": f"Failed to build blog: {response.status_code}"}
except httpx.ConnectError:
return {
"error": "ai.log server is not running",
"hint": "Please start ai.log server: cd log && cargo run -- mcp --port 8002",
"details": "Connection refused to http://localhost:8002"
}
except Exception as e:
return {"error": f"ai.log connection failed: {str(e)}"}
@self.app.get("/log_get_post", operation_id="log_get_post")
async def log_get_post(slug: str) -> Dict[str, Any]:
"""Get blog post content by slug"""
logger.info(f"📝 [ai.log] Getting post: {slug}")
try:
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.post(
"http://localhost:8002/mcp/tools/call",
json={
"jsonrpc": "2.0",
"id": "log_get_post",
"method": "call_tool",
"params": {
"name": "get_post_content",
"arguments": {
"slug": slug
}
}
}
)
if response.status_code == 200:
result = response.json()
if result.get("error"):
return {"error": result["error"]["message"]}
return result.get("result", {})
else:
return {"error": f"Failed to get post: {response.status_code}"}
except httpx.ConnectError:
return {
"error": "ai.log server is not running",
"hint": "Please start ai.log server: cd log && cargo run -- mcp --port 8002",
"details": "Connection refused to http://localhost:8002"
}
except Exception as e:
return {"error": f"ai.log connection failed: {str(e)}"}
@self.app.get("/log_system_status", operation_id="log_system_status")
async def log_system_status() -> Dict[str, Any]:
"""Check ai.log system status"""
try:
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.get("http://localhost:8002/health")
if response.status_code == 200:
return {
"status": "online",
"health": response.json(),
"log_dir": str(self.log_dir)
}
else:
return {
"status": "error",
"error": f"Health check failed: {response.status_code}"
}
except Exception as e:
return {
"status": "offline",
"error": f"ai.log is not running: {str(e)}",
"hint": "Start ai.log with: cd log && cargo run -- mcp --port 8002"
}
@self.app.post("/log_ai_content", operation_id="log_ai_content")
async def log_ai_content(user_id: str, topic: str = "daily thoughts") -> Dict[str, Any]:
"""Generate AI content for blog from memories and create post"""
logger.info(f"📝 [ai.log] Generating AI content for: {topic}")
try:
# Get contextual memories for the topic
memories = await get_contextual_memories(topic, limit=5)
# Get AI provider
ai_provider = create_ai_provider()
# Build content from memories
memory_context = ""
for group_name, mem_list in memories.items():
memory_context += f"\n## {group_name}\n"
for mem in mem_list:
memory_context += f"- {mem['content']}\n"
# Generate blog content
prompt = f"""Based on the following memories and context, write a thoughtful blog post about {topic}.
Memory Context:
{memory_context}
Please write a well-structured blog post in Markdown format with:
1. An engaging title
2. Clear structure with headings
3. Personal insights based on the memories
4. A conclusion that ties everything together
Focus on creating content that reflects personal growth and learning from these experiences."""
content = ai_provider.generate_response(prompt, "You are a thoughtful blogger who creates insightful content.")
# Extract title from content (first heading)
lines = content.split('\n')
title = topic.title()
for line in lines:
if line.startswith('# '):
title = line[2:].strip()
content = '\n'.join(lines[1:]).strip() # Remove title from content
break
# Create the blog post
return await log_create_post(
title=title,
content=content,
tags=["AI", "thoughts", "daily"]
)
except Exception as e:
return {"error": f"Failed to generate AI content: {str(e)}"}
@self.app.post("/log_translate_document", operation_id="log_translate_document")
async def log_translate_document(
input_file: str,
target_lang: str,
source_lang: Optional[str] = None,
output_file: Optional[str] = None,
model: str = "qwen2.5:latest",
ollama_endpoint: str = "http://localhost:11434"
) -> Dict[str, Any]:
"""Translate markdown documents using Ollama via ai.log"""
logger.info(f"🌍 [ai.log] Translating document: {input_file} -> {target_lang}")
try:
async with httpx.AsyncClient(timeout=60.0) as client: # Longer timeout for translation
response = await client.post(
"http://localhost:8002/mcp/tools/call",
json={
"jsonrpc": "2.0",
"id": "log_translate_document",
"method": "call_tool",
"params": {
"name": "translate_document",
"arguments": {
"input_file": input_file,
"target_lang": target_lang,
"source_lang": source_lang,
"output_file": output_file,
"model": model,
"ollama_endpoint": ollama_endpoint
}
}
}
)
if response.status_code == 200:
result = response.json()
if result.get("error"):
return {"error": result["error"]["message"]}
return {
"success": True,
"message": "Document translated successfully",
"input_file": input_file,
"target_lang": target_lang,
"output_file": result.get("result", {}).get("output_file")
}
else:
return {"error": f"Failed to translate document: {response.status_code}"}
except httpx.ConnectError:
return {
"error": "ai.log server is not running",
"hint": "Please start ai.log server: cd log && cargo run -- mcp --port 8002",
"details": "Connection refused to http://localhost:8002"
}
except Exception as e:
return {"error": f"ai.log translation failed: {str(e)}"}
@self.app.post("/log_generate_docs", operation_id="log_generate_docs")
async def log_generate_docs(
doc_type: str, # "readme", "api", "structure", "changelog"
source_path: Optional[str] = None,
output_path: Optional[str] = None,
with_ai: bool = True,
include_deps: bool = False,
format_type: str = "markdown"
) -> Dict[str, Any]:
"""Generate documentation using ai.log's doc generation features"""
logger.info(f"📚 [ai.log] Generating {doc_type} documentation")
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"http://localhost:8002/mcp/tools/call",
json={
"jsonrpc": "2.0",
"id": "log_generate_docs",
"method": "call_tool",
"params": {
"name": "generate_documentation",
"arguments": {
"doc_type": doc_type,
"source_path": source_path or ".",
"output_path": output_path,
"with_ai": with_ai,
"include_deps": include_deps,
"format_type": format_type
}
}
}
)
if response.status_code == 200:
result = response.json()
if result.get("error"):
return {"error": result["error"]["message"]}
return {
"success": True,
"message": f"{doc_type.title()} documentation generated successfully",
"doc_type": doc_type,
"output_path": result.get("result", {}).get("output_path")
}
else:
return {"error": f"Failed to generate documentation: {response.status_code}"}
except httpx.ConnectError:
return {
"error": "ai.log server is not running",
"hint": "Please start ai.log server: cd log && cargo run -- mcp --port 8002",
"details": "Connection refused to http://localhost:8002"
}
except Exception as e:
return {"error": f"ai.log documentation generation failed: {str(e)}"}
def get_server(self) -> FastApiMCP: def get_server(self) -> FastApiMCP:
"""Get the FastAPI MCP server instance""" """Get the FastAPI MCP server instance"""
return self.server return self.server