From 0f186895396ecb18c7b75d92fd3b15eaec6e301d Mon Sep 17 00:00:00 2001 From: syui Date: Mon, 2 Jun 2025 06:21:24 +0900 Subject: [PATCH] update --- .claude/settings.local.json | 12 +- DEVELOPMENT_STATUS.md | 25 +- README.md | 45 +++- aishell.md | 63 +++++ docs/ai_shell_integration_summary.md | 218 ++++++++++++++++++ pyproject.toml | 1 + src/aigpt.egg-info/PKG-INFO | 1 + src/aigpt.egg-info/SOURCES.txt | 2 + src/aigpt.egg-info/requires.txt | 1 + .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 493 bytes .../__pycache__/ai_provider.cpython-313.pyc | Bin 0 -> 8706 bytes .../card_integration.cpython-313.pyc | Bin 0 -> 8686 bytes src/aigpt/__pycache__/cli.cpython-313.pyc | Bin 0 -> 34140 bytes src/aigpt/__pycache__/config.cpython-313.pyc | Bin 0 -> 6583 bytes src/aigpt/__pycache__/fortune.cpython-313.pyc | Bin 0 -> 5403 bytes .../__pycache__/mcp_server.cpython-313.pyc | Bin 0 -> 18532 bytes src/aigpt/__pycache__/memory.cpython-313.pyc | Bin 0 -> 9266 bytes src/aigpt/__pycache__/models.cpython-313.pyc | Bin 0 -> 4066 bytes src/aigpt/__pycache__/persona.cpython-313.pyc | Bin 0 -> 8227 bytes .../__pycache__/relationship.cpython-313.pyc | Bin 0 -> 7667 bytes .../__pycache__/scheduler.cpython-313.pyc | Bin 0 -> 17030 bytes .../__pycache__/transmission.cpython-313.pyc | Bin 0 -> 6316 bytes src/aigpt/ai_provider.py | 4 +- src/aigpt/card_integration.py | 150 ++++++++++++ src/aigpt/cli.py | 101 +++++++- src/aigpt/mcp_server.py | 197 ++++++++++++++-- src/aigpt/mcp_server_simple.py | 146 ++++++++++++ src/aigpt/models.py | 4 +- 28 files changed, 935 insertions(+), 35 deletions(-) create mode 100644 aishell.md create mode 100644 docs/ai_shell_integration_summary.md create mode 100644 src/aigpt/__pycache__/__init__.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/ai_provider.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/card_integration.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/cli.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/config.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/fortune.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/mcp_server.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/memory.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/models.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/persona.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/relationship.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/scheduler.cpython-313.pyc create mode 100644 src/aigpt/__pycache__/transmission.cpython-313.pyc create mode 100644 src/aigpt/card_integration.py create mode 100644 src/aigpt/mcp_server_simple.py diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5f8fa1d..bfd8f6c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,17 @@ "Bash(mv:*)", "Bash(mkdir:*)", "Bash(chmod:*)", - "Bash(git submodule:*)" + "Bash(git submodule:*)", + "Bash(source:*)", + "Bash(pip install:*)", + "Bash(/Users/syui/.config/syui/ai/gpt/venv/bin/aigpt shell)", + "Bash(/Users/syui/.config/syui/ai/gpt/venv/bin/aigpt server --model qwen2.5-coder:7b --port 8001)", + "Bash(/Users/syui/.config/syui/ai/gpt/venv/bin/python -c \"import fastapi_mcp; help(fastapi_mcp.FastApiMCP)\")", + "Bash(find:*)", + "Bash(/Users/syui/.config/syui/ai/gpt/venv/bin/pip install -e .)", + "Bash(/Users/syui/.config/syui/ai/gpt/venv/bin/aigpt fortune)", + "Bash(lsof:*)", + "Bash(/Users/syui/.config/syui/ai/gpt/venv/bin/python -c \"\nfrom src.aigpt.mcp_server import AIGptMcpServer\nfrom pathlib import Path\nimport uvicorn\n\ndata_dir = Path.home() / '.config' / 'syui' / 'ai' / 'gpt' / 'data'\ndata_dir.mkdir(parents=True, exist_ok=True)\n\ntry:\n server = AIGptMcpServer(data_dir)\n print('MCP Server created successfully')\n print('Available endpoints:', [route.path for route in server.app.routes])\nexcept Exception as e:\n print('Error:', e)\n import traceback\n traceback.print_exc()\n\")" ], "deny": [] } diff --git a/DEVELOPMENT_STATUS.md b/DEVELOPMENT_STATUS.md index 25357e7..e1896e8 100644 --- a/DEVELOPMENT_STATUS.md +++ b/DEVELOPMENT_STATUS.md @@ -1,4 +1,4 @@ -# ai.gpt 開発状況 (2025/01/06) +# ai.gpt 開発状況 (2025/01/06 更新) ## 現在の状態 @@ -20,6 +20,7 @@ - `config` - 設定管理 - `schedule` - スケジューラー管理 - `server` - MCP Server起動 + - `shell` - インタラクティブシェル(ai.shell統合) 3. **データ管理** - 保存場所: `~/.config/aigpt/` @@ -32,8 +33,16 @@ - バックグラウンド実行可能 5. **MCP Server** - - 9種類のツールを公開 + - 14種類のツールを公開(ai.gpt: 9種類、ai.shell: 5種類) - Claude Desktopなどから利用可能 + - ai.card統合オプション(--enable-card) + +6. **ai.shell統合** + - インタラクティブシェルモード + - シェルコマンド実行(!command形式) + - AIコマンド(analyze, generate, explain) + - aishell.md読み込み機能 + - 高度な補完機能(prompt-toolkit) ## 🚧 未実装・今後の課題 @@ -82,14 +91,14 @@ ### 1. 自律送信を実装する場合 ```python -# src/ai_gpt/transmission.py を編集 +# src/aigpt/transmission.py を編集 # atproto-python ライブラリを追加 # _handle_transmission_check() メソッドを更新 ``` ### 2. ai.botと連携する場合 ```python -# 新規ファイル: src/ai_gpt/bot_connector.py +# 新規ファイル: src/aigpt/bot_connector.py # ai.botのAPIエンドポイントにHTTPリクエスト ``` @@ -99,6 +108,13 @@ # pytest設定を追加 ``` +### 4. ai.shellの問題を修正する場合 +```python +# src/aigpt/cli.py の shell コマンド +# prompt-toolkitのターミナル検出問題を回避 +# 代替: simple input() または click.prompt() +``` + ## 設計思想の要点(AI向け) 1. **唯一性(yui system)**: 各ユーザーとAIの関係は1:1で、改変不可能 @@ -113,5 +129,6 @@ - **AI統合**: Ollama (ローカル) / OpenAI API - **データ形式**: JSON(将来的にSQLite検討) - **認証**: atproto DID(未実装だが設計済み) +- **統合**: ai.shell(Rust版から移行)、ai.card(MCP連携) このファイルを参照することで、次回の開発がスムーズに始められます。 \ No newline at end of file diff --git a/README.md b/README.md index 42f98ee..bdb8f3c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,9 @@ ## インストール ```bash -cd ai_gpt +# Python仮想環境を推奨 +python -m venv venv +source venv/bin/activate # Windows: venv\Scripts\activate pip install -e . ``` @@ -93,6 +95,29 @@ aigpt relationships - 時間経過で自然減衰 - 大きなネガティブな相互作用で破壊される可能性 +## ai.shell統合 + +インタラクティブシェルモード(Claude Code風の体験): + +```bash +aigpt shell + +# シェル内で使えるコマンド: +# help - コマンド一覧 +# ! - シェルコマンド実行(例: !ls, !pwd) +# analyze - ファイルをAIで分析 +# generate - コード生成 +# explain - 概念の説明 +# load - aishell.mdプロジェクトファイルを読み込み +# status - AI状態確認 +# fortune - AI運勢確認 +# clear - 画面クリア +# exit/quit - 終了 + +# 通常のメッセージも送れます +ai.shell> こんにちは、今日は何をしましょうか? +``` + ## MCP Server ### サーバー起動 @@ -105,6 +130,9 @@ aigpt server --model gpt-4o-mini --provider openai # カスタムポート aigpt server --port 8080 + +# ai.card統合を有効化 +aigpt server --enable-card ``` ### AIプロバイダーを使った会話 @@ -120,6 +148,7 @@ aigpt chat "did:plc:xxxxx" "今日の調子はどう?" --provider openai --mod サーバーが起動すると、以下のツールがAIから利用可能になります: +**ai.gpt ツール:** - `get_memories` - アクティブな記憶を取得 - `get_relationship` - 特定ユーザーとの関係を取得 - `get_all_relationships` - すべての関係を取得 @@ -130,6 +159,20 @@ aigpt chat "did:plc:xxxxx" "今日の調子はどう?" --provider openai --mod - `summarize_memories` - 記憶を要約 - `run_maintenance` - メンテナンス実行 +**ai.shell ツール:** +- `execute_command` - シェルコマンド実行 +- `analyze_file` - ファイルのAI分析 +- `write_file` - ファイル書き込み +- `read_project_file` - プロジェクトファイル読み込み +- `list_files` - ファイル一覧 + +**ai.card ツール(--enable-card時):** +- `get_user_cards` - ユーザーのカード取得 +- `draw_card` - カードを引く(ガチャ) +- `get_card_details` - カード詳細情報 +- `sync_cards_atproto` - atproto同期 +- `analyze_card_collection` - コレクション分析 + ## 環境変数 `.env`ファイルを作成して設定: diff --git a/aishell.md b/aishell.md new file mode 100644 index 0000000..136f61d --- /dev/null +++ b/aishell.md @@ -0,0 +1,63 @@ +# ai.shell プロジェクト仕様書 + +## 概要 +ai.shellは、AIを活用したインタラクティブなシェル環境です。Claude Codeのような体験を提供し、プロジェクトの目標と仕様をAIが理解して、開発を支援します。 + +## 主要機能 + +### 1. インタラクティブシェル +- AIとの対話型インターフェース +- シェルコマンドの実行(!command形式) +- 高度な補完機能 +- コマンド履歴 + +### 2. AI支援機能 +- **analyze **: ファイルの分析 +- **generate **: コード生成 +- **explain **: 概念の説明 +- **load**: プロジェクト仕様(このファイル)の読み込み + +### 3. ai.gpt統合 +- 関係性ベースのAI人格 +- 記憶システム +- 運勢システムによる応答の変化 + +## 使用方法 + +```bash +# ai.shellを起動 +aigpt shell + +# プロジェクト仕様を読み込み +ai.shell> load + +# ファイルを分析 +ai.shell> analyze src/main.py + +# コードを生成 +ai.shell> generate Python function to calculate fibonacci + +# シェルコマンドを実行 +ai.shell> !ls -la + +# AIと対話 +ai.shell> How can I improve this code? +``` + +## 技術スタック +- Python 3.10+ +- prompt-toolkit(補完機能) +- fastapi-mcp(MCP統合) +- ai.gpt(人格・記憶システム) + +## 開発目標 +1. Claude Codeのような自然な開発体験 +2. AIがプロジェクトコンテキストを理解 +3. シェルコマンドとAIの seamless な統合 +4. 開発者の生産性向上 + +## 今後の展開 +- ai.cardとの統合(カードゲームMCPサーバー) +- より高度なプロジェクト理解機能 +- 自動コード修正・リファクタリング +- テスト生成・実行 \ No newline at end of file diff --git a/docs/ai_shell_integration_summary.md b/docs/ai_shell_integration_summary.md new file mode 100644 index 0000000..1e88c69 --- /dev/null +++ b/docs/ai_shell_integration_summary.md @@ -0,0 +1,218 @@ +# ai.shell統合作業完了報告 (2025/01/06) + +## 作業概要 +ai.shellのRust実装をai.gptのPython実装に統合し、Claude Code風のインタラクティブシェル環境を実現。 + +## 実装完了機能 + +### 1. aigpt shellコマンド +**場所**: `src/aigpt/cli.py` - `shell()` 関数 + +**機能**: +```bash +aigpt shell # インタラクティブシェル起動 +``` + +**シェル内コマンド**: +- `help` - コマンド一覧表示 +- `!` - シェルコマンド実行(例: `!ls`, `!pwd`) +- `analyze ` - ファイルをAIで分析 +- `generate ` - コード生成 +- `explain ` - 概念説明 +- `load` - aishell.md読み込み +- `status`, `fortune`, `relationships` - AI状態確認 +- `clear` - 画面クリア +- `exit`/`quit` - 終了 +- その他のメッセージ - AIとの直接対話 + +**実装の特徴**: +- prompt-toolkit使用(補完・履歴機能) +- ただしターミナル環境依存の問題あり(後で修正必要) +- 現在は`input()`ベースでも動作 + +### 2. MCPサーバー統合 +**場所**: `src/aigpt/mcp_server.py` + +**FastApiMCP実装パターン**: +```python +# FastAPIアプリ作成 +self.app = FastAPI(title="AI.GPT Memory and Relationship System") + +# FastApiMCPサーバー作成 +self.server = FastApiMCP(self.app) + +# エンドポイント登録 +@self.app.get("/get_memories", operation_id="get_memories") +async def get_memories(limit: int = 10): + # ... + +# MCPマウント +self.server.mount() +``` + +**公開ツール (14個)**: + +**ai.gpt系 (9個)**: +- `get_memories` - アクティブメモリ取得 +- `get_relationship` - 特定ユーザーとの関係取得 +- `get_all_relationships` - 全関係取得 +- `get_persona_state` - 人格状態取得 +- `process_interaction` - ユーザー対話処理 +- `check_transmission_eligibility` - 送信可能性チェック +- `get_fortune` - AI運勢取得 +- `summarize_memories` - メモリ要約作成 +- `run_maintenance` - 日次メンテナンス実行 + +**ai.shell系 (5個)**: +- `execute_command` - シェルコマンド実行 +- `analyze_file` - ファイルAI分析 +- `write_file` - ファイル書き込み(バックアップ付き) +- `read_project_file` - aishell.md等の読み込み +- `list_files` - ディレクトリファイル一覧 + +### 3. ai.card統合対応 +**場所**: `src/aigpt/card_integration.py` + +**サーバー起動オプション**: +```bash +aigpt server --enable-card # ai.card機能有効化 +``` + +**ai.card系ツール (5個)**: +- `get_user_cards` - ユーザーカード取得 +- `draw_card` - ガチャでカード取得 +- `get_card_details` - カード詳細情報 +- `sync_cards_atproto` - atproto同期 +- `analyze_card_collection` - コレクション分析 + +### 4. プロジェクト仕様書 +**場所**: `aishell.md` + +Claude.md的な役割で、プロジェクトの目標と仕様を記述。`load`コマンドでAIが読み取り可能。 + +## 技術実装詳細 + +### ディレクトリ構造 +``` +src/aigpt/ +├── cli.py # shell関数追加 +├── mcp_server.py # FastApiMCP実装 +├── card_integration.py # ai.card統合 +└── ... # 既存ファイル +``` + +### 依存関係追加 +`pyproject.toml`: +```toml +dependencies = [ + # ... 既存 + "prompt-toolkit>=3.0.0", # 追加 +] +``` + +### 名前規則の統一 +- MCP server名: `aigpt` (ai-gptから変更) +- パッケージ名: `aigpt` +- コマンド名: `aigpt shell` + +## 動作確認済み + +### CLI動作確認 +```bash +# 基本機能 +aigpt shell +# シェル内で +ai.shell> help +ai.shell> !ls +ai.shell> analyze README.md # ※AI provider要設定 +ai.shell> load +ai.shell> exit + +# MCPサーバー +aigpt server --model qwen2.5-coder:7b --port 8001 +# -> http://localhost:8001/docs でAPI確認可能 +# -> /mcp エンドポイントでMCP接続可能 +``` + +### エラー対応済み +1. **Pydantic日付型エラー**: `models.py`で`datetime.date`インポート追加 +2. **FastApiMCP使用法**: サンプルコードに基づき正しい実装パターンに修正 +3. **prompt関数名衝突**: `prompt_toolkit.prompt`を`ptk_prompt`にリネーム + +## 既知の課題と今後の改善点 + +### 1. prompt-toolkit環境依存問題 +**症状**: ターミナル環境でない場合にエラー +**対処法**: 環境検出して`input()`にフォールバック +**場所**: `src/aigpt/cli.py` - `shell()` 関数 + +### 2. AI provider設定 +**現状**: ollamaのqwen2.5モデルが必要 +**対処法**: +```bash +ollama pull qwen2.5 +# または +aigpt shell --model qwen2.5-coder:7b +``` + +### 3. atproto実装 +**現状**: ai.cardのatproto機能は未実装 +**今後**: 実際のatproto API連携実装 + +## 次回開発時の推奨アプローチ + +### 1. このドキュメントの活用 +```bash +# このファイルを読み込み +cat docs/ai_shell_integration_summary.md +``` + +### 2. 環境セットアップ +```bash +cd /Users/syui/ai/gpt +python -m venv venv +source venv/bin/activate +pip install -e . +``` + +### 3. 動作確認 +```bash +# shell機能 +aigpt shell + +# MCP server +aigpt server --model qwen2.5-coder:7b +``` + +### 4. 主要設定ファイル確認場所 +- CLI実装: `src/aigpt/cli.py` +- MCP実装: `src/aigpt/mcp_server.py` +- 依存関係: `pyproject.toml` +- プロジェクト仕様: `aishell.md` + +## アーキテクチャ設計思想 + +### yui system適用 +- **唯一性**: 各ユーザーとの関係は1:1 +- **不可逆性**: 関係性破壊は修復不可能 +- **現実反映**: ゲーム→現実の循環的影響 + +### fastapi_mcp統一基盤 +- 各AI(gpt, shell, card)を統合MCPサーバーで公開 +- FastAPIエンドポイント → MCPツール自動変換 +- Claude Desktop, Cursor等から利用可能 + +### 段階的実装完了 +1. ✅ ai.shell基本機能 → Python CLI +2. ✅ MCP統合 → 外部AI連携 +3. 🔧 prompt-toolkit最適化 → 環境対応 +4. 🔧 atproto実装 → 本格的SNS連携 + +## 成果サマリー + +**実装済み**: Claude Code風の開発環境 +**技術的成果**: Rust→Python移行、MCP統合、ai.card対応 +**哲学的一貫性**: yui systemとの整合性維持 +**利用可能性**: 即座に`aigpt shell`で体験可能 + +この統合により、ai.gptは単なる会話AIから、開発支援を含む総合的なAI環境に進化しました。 \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 56611de..555a326 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "uvicorn>=0.23.0", "apscheduler>=3.10.0", "croniter>=1.3.0", + "prompt-toolkit>=3.0.0", ] [project.scripts] diff --git a/src/aigpt.egg-info/PKG-INFO b/src/aigpt.egg-info/PKG-INFO index 970c7ca..90141ef 100644 --- a/src/aigpt.egg-info/PKG-INFO +++ b/src/aigpt.egg-info/PKG-INFO @@ -15,3 +15,4 @@ Requires-Dist: openai>=1.0.0 Requires-Dist: uvicorn>=0.23.0 Requires-Dist: apscheduler>=3.10.0 Requires-Dist: croniter>=1.3.0 +Requires-Dist: prompt-toolkit>=3.0.0 diff --git a/src/aigpt.egg-info/SOURCES.txt b/src/aigpt.egg-info/SOURCES.txt index 0fe3aa5..d2e112d 100644 --- a/src/aigpt.egg-info/SOURCES.txt +++ b/src/aigpt.egg-info/SOURCES.txt @@ -2,10 +2,12 @@ README.md pyproject.toml src/aigpt/__init__.py src/aigpt/ai_provider.py +src/aigpt/card_integration.py src/aigpt/cli.py src/aigpt/config.py src/aigpt/fortune.py src/aigpt/mcp_server.py +src/aigpt/mcp_server_simple.py src/aigpt/memory.py src/aigpt/models.py src/aigpt/persona.py diff --git a/src/aigpt.egg-info/requires.txt b/src/aigpt.egg-info/requires.txt index e12e509..c9ab0c4 100644 --- a/src/aigpt.egg-info/requires.txt +++ b/src/aigpt.egg-info/requires.txt @@ -10,3 +10,4 @@ openai>=1.0.0 uvicorn>=0.23.0 apscheduler>=3.10.0 croniter>=1.3.0 +prompt-toolkit>=3.0.0 diff --git a/src/aigpt/__pycache__/__init__.cpython-313.pyc b/src/aigpt/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55b7911068d613f2030bb3b00b43d2f9879f2867 GIT binary patch literal 493 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 literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/card_integration.cpython-313.pyc b/src/aigpt/__pycache__/card_integration.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1218d1b2bf39c36c1e3a044ce6e64793dfd3a67f GIT binary patch literal 8686 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 Xao%-;X?Dmkz;V{apMFU&XDj_T(^Y2R literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/cli.cpython-313.pyc b/src/aigpt/__pycache__/cli.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..006ac3a16cff23bb28606027aefa6fbcba7a28ab GIT binary patch literal 34140 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 SA)Y7F3!LrLR|FxO=l=i|e1G-; literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/fortune.cpython-313.pyc b/src/aigpt/__pycache__/fortune.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8dd19b5bf388f1f425383a6829f7f3ff071b7cd GIT binary patch literal 5403 zcmb7IU2Gf25nl4{_)ki-WSSBs>nO^SXg0-AWUDZ07a{p0xr-8H&1?O0iEfDmy3f4NLs)^AC%aDfjo8Q z$fIPLanO;tySKADx3lxj?Cd@A`#l7x*`rc z2ON*!o^(TxchWcM@3(UxtDhA-CNCg(X+zc*_Z$A|0t8dxhkh6}-!~%4VpdYBs>8Ox zKA77gPEAQ^H792`?uV(in9Ag6y1Jp}`WYzk_h=D{>*Qi1nzTYmFlMipkR-|DPMEY8 zpK9|btUVy(E^EYk-AN`)2w~5`ssxx_o0BBt4eO(=*1>9nCs`7=kIwt6S}A>3z_f7} zMdb}&wKp}D%Y#L&F@`f=$Yw!v!=07X3(%IR;Yy`saau~H3e)#7xSX6}KC# zBITz{(F$tE^G45}qylK#qs-0b_K3MX*%@_@LetR1R&{FM%p8i($vG9ohfY@vP_a^< z)!zsLl>sPTCwiEF)33L66@RU@9a>=TyIS={C4VY_GgCw$hZ@qpD@uRBSS6AL4Y6ciQQR33?L!?{~=QrjjNKjkYpOt}fg>txm2 zQ1-TI-nOzgrg>v!?+(qo<3VuiVg{hBg?g5nmVa?CICy8@gKd!;b8pWTgYW!mk=6V5 z|K-X@SN@#Wns+Up{W@`=oEW;77`n6j?rYkqAD0rZmJ^d&VzQL@+2Vy_NNe8tpzpBO zcjoqPC>CG5F>-CBxU z+5le@ju(fLb`zF|E3llXxe~0m<}T|(1QuZHFrAc}s5#L%qG1ztl8ajfr{Ef30PJr3 z+W>a=bD-|7LftdpA&9R@&p=%*SU{&Scqg8z(}j#=V$-nwg0@2^-44Y=jIqFW#IS)#$td>$$W_ zaW@sTh6(rz;^8PzK$Y52@C^8$;zm)zZ=eA1Hv|`0J-Xvo`-ko2XulTi|1^3?^R+LW z)gztdNUs*@T|TrD>0OB&UAUmfcHKJu;qh{8K#L8OV#gLPyc^PdyxtyP4F6#ipxk-* zv4;e!`crjBH@A-B*4ws zNEE?5fY3UqPny|H7n#6SnKWL@53C-7X&?BGg-Dr6+9vSCHwie*fUOs1foHMLT_Gp1 zvIBljfsU7mM}mR*8$C zWyG4dURwy|5rcJ(`ntQ|OELi>_zy7a8YDekBp#UWI3uY%+%B?`EK#^M@M<9=&h;o( z=%WbbhlpYfcED07I@aljPV7g|du4vtDJqFSS1;4TZ1!@LKftHqN+P9a)f|M-Y@tv~ z8I)4{1Op=S;mO=4U0Naf9#_3O5=y_J9Z@bZNtSQYy`1N-SmO zgReZT+fja=*|TqNJWzQNXR1#3!%NLz4e>^lGlglxiFHm^Df$aNfyE#eC$T^i7;H|4 zRhr4kA$W#72a^+e3VCi*EM_L=RKqul&a{Zz94%z)h}>}_z5*U8t7%{M%6d*1OaaC&pgjj?NErRE(A z?p1H|2mYJ>l6QyRwC%LXibR~U*fTYv~+wBcrQ@Ay3|<;_b&L?93&KZ|0hdB zT6>?iwQs?#hoj|iLJKEM_G+YU#TC|DBMS|Bw0p(1ebpQOVC3dV$s5=Cu3P>O{Y%pt ze|*K)zS_`Mw3Qm7dQ(@qX}{LAe_1Rw9oBn}lzWD?p5ePgTF;2q+_l=X6G}a}y%;S8 zW19wdA1-&F(YnvvjcVQJuZ^vC_biUV0vq|_zEVS%9`9Xg?AAeL?=^3^k=GjemFUR* z#!;Q`yyn+~d~v80+);Pbh5L;o5BTm|fjU`TK6(c63VwuBxV z_{QsK@+>&l8cA2;E&m(-#hm8iYqy>IuJ%=z?=9~e-m)vKxxy=v-Ag}R8oPa9rDgEG z>!jWgS_sfqu#IW$r>?T-bLbO~?mz)|B3r#XQRPaDcax-Du=fIH1!e+gnIy3FXW@}! z1qXq{S=b{uK!(G7g4u!-o?NvjcOQp`*ElBYn+P)%BoSpWa2$9rH4(hf@?k5ty!5n@A4_t37L0BV@7uX7#KNW~WU0 z&tUd|Nu|L!XSfRR`2#K%({K-gpqGUU?hJfJfy;_yc+wC$Aa23u6~ie_L;vMD=q$)n zImqMEQubf}MEdWb;Z}Ob?zJt% z*Hre#;a~5LFAOi{HD65c*#)g>&DXWs5C%^-0SvoOmDXpOYoI0BmO}l_{iCDT+xK4P&tt3k+@2F3qZv0T$Lm!S9^`HVfX z#&X>DV)J7H)wcl`ch>f8vzv>rg${GuiUW@cRBO~09YXqv>nL7vWa(9kuCq|#( zbL9;v9L^d1kY*7k zE|0vL3Wn==WkXNU3@{+F69t3(8e_BBJ|joIBHqu5|4Y)Rk-jfT_zM#GoCN+&+MhUb NHe1WL1WQxe{{SHZsgwW! literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/mcp_server.cpython-313.pyc b/src/aigpt/__pycache__/mcp_server.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c6506c9b08716738808f5de1c9351f63b8feb3f GIT binary patch literal 18532 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 CN=I1$ literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/memory.cpython-313.pyc b/src/aigpt/__pycache__/memory.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b7f8c3c229240efe15e4893e3ec38330b4d562b GIT binary patch literal 9266 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 literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/persona.cpython-313.pyc b/src/aigpt/__pycache__/persona.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f1b4d15a593adcfa9a99f5a7fd6af6ce7f977fe GIT binary patch literal 8227 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 XC;LAqgWuX0&8A~t!S@xDzNG&Hc|kvp literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/relationship.cpython-313.pyc b/src/aigpt/__pycache__/relationship.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ebbc45e515184964e507923ae0808d5a12b1885 GIT binary patch literal 7667 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 ai7y?UD&rRnL|>j)UDBv(9y1uz)A+v?c4~nD literal 0 HcmV?d00001 diff --git a/src/aigpt/__pycache__/transmission.cpython-313.pyc b/src/aigpt/__pycache__/transmission.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb3f15602e8ff6fdaa2c6f9b5564da34fe2c891f GIT binary patch literal 6316 zcmb7IYit|G5#A$@FOd`}QKlqHwoddbtR>11IdKvtwqiT79#IUQ**K9}_DmkBvyXSm z-qEUr{4n~XP=Wx7(KwH`K++ay93TaZJ^&SH0q395Upgx(nX7{sNE^Wa6x%?M{OZgd zNl~(7v`c9pv%9mqGdthR-o0S3i9ot?^vRjG!i0Qm3y;Lxk7kC3D5A3afUKb zI>#Mj4(i~L#*RD3Sjt*;*SKrUP2FQ2>KXG=uSIi=`>2n1^PcghF+cSK&CPqq17krN zJaN6%0}(Tb9$NvgtSRYj+&EQ^%OtCSN;x~ix}wWM)H zQPYHi$Z4XIla#_8WHOmxOm9xmMO`Y2P-jmI`b@%UvJ;Y~o9qcGtDD~EXLaB#$aff+ z0fPc5Q5KM-%}BG*_M8@}1_QG8w(Q(pZQOeoDp$xf0cjBG;2G-V9hBvr(=MK!b`P?= zD@M+<)Wf@DgnD@ozTk9t z(9Jhn)E2%K=+&{v5!MQ0&v}Nn@+~w{2qhwBv{8D;>%zMegoPK5R0$e4HzO*Xpyaq2 zb&eYz<#b!-dR?qqgQ(?s(=%(!#FDO%$w@TptV#q6tmFT^Uqg0w*9jkOhEX&GnYpd8KO?J!OkR>jGuTMhOb>Jv;NF?Of~ZeGU8JTr zlTn1Cn8}!K>;*~$<_o#Zx~X^{HBrv5Eve!8IgT^?2GSY`dO({mNdtm3P?*&RG@6Af z)*87aXXkM_Ns)9|uIEgH&8%rXSsMU0PPAi?T_TnC9j^x}9o*}|%I^N<{U?mwCl=XH zz41z7AC|FO(e35v-kZ_AAA47#2Nv1a{S|MZ?BxtE_n9|-4|tGlU7^rG!?09efyxz7 zW(vxUDOFeST2DTbxFU7F zJd3{?#=kX<6T{h_jdx8dJi_V*e7zOp}Q_>;FL{`SSIFaK`(P4?4p{97k!9c7kVPOUmgVBpJ^Q=he*`d$NbC4cE2 z?s9(8)ij*&tkXTp-O~NXP`N^;>h}V)S2u9zs-|_?tGOAHs_U^f&eGndT1|t~nW_T= z?|PiIoQj)wH)z!T5PiC<`t&Sx^1?-N3w_Sf9@q=zAiIO-NH|Qlq~t_JH$6EqFO*~* z)w>f1p^oV;$V@g@D$ZI;HJzGH=^h|9Diy#$gEiNK!PZKCaO@6b_#|YP$iIUvZ|*9$ z_8F~xSF=kaH-jf`Ft-BXH%4C@MLj=aL?5}@du{k{+Ream&};KarW`!+&)^ALpKtFO zb~!(BH4R5Rrkjd-i7M;#if6X;Iti66ZF&<8r|NbF6RW9a=L(Z%c7eZL7&ty7W-ovX zlCm>g3Gt2-sK|-3RFKZeqUGlsp)TRHkkSm&20yKtoq)URWSOaPNK|dgTO+K~!j3F1 zzu8Plmh^e^@D^70@M(A#v|h--03+@13|$^7NBWIOe>pN@L`GI3ql@0vW)kjL4AE{F zWqR=787Y@-lp@-MCHFsqC2c8p9I+v-VO=AR6`0LY^r2dEY%ymOD^2+}Yy+BWJ5iil zaH`wy+UyjDCmWK5^o+u$9Vzt98&V2g+J;m+y8v?JS;duh?IJK6VRl2}T~C}P#2V+W zj&r8kHVlGhD*7Oe_tfWl?YX?qE)z`)dqzY>q!55|LOnp$)f&v`9MnNDUrIBizUN`lPI2(#cXL~uE3PMGiIM!CEw>ktnYXGI8yk58Kz)BPY@{3@ycEFtE&ddnP1#1Zm&(DcCaz>XWTAX&cM++L{O zk=l8!XaY8By=Tn9E%4`|vy!le_7BJaenOEqUU~JEa%i6s+IMxN92zu2gOy18?Qq*W zfy;q%IBA5FE8+c%u1b5?JFi@RrQCkNXg^SHpD@}dZoIJ4o?Z;Bx=B~h`+e{Am1F%z ztiK!^He$m!nB~~;a%^ld1l@s_H=cU+sj`0n{wmS4izBOH5^i5?UJa4vNIBSJ1baR> zvJ%`^iB3RY!0z*t=2$t%89}Zb++zgyTurV7k5r=5Fv4q(aFO<>7e{V~qKnNIPSOb! zxJN5xvml-<0%R@KgO5)wu$-#VHw``Fq~?Ym27YNK?MNeR{f}T*=eY@_ZNa7feyMMl zr0~_TA+5paW4>gmuthlw&Ml%{P6BoL9`!)ew%JVhGz|n%#h3K;ay!NbmbxjMkCleux zm~A>0bsIt&@01g>VsgKi7Vb96uUqE9hdNy)3GXHK9B zKxV?V1}_a~2sEBr!`P|;Mk3Ub%{ma-GMe0E>`7p!{TZ@LV|0d|^XaXE*CB9(U8#BDIvbWdp_I}X!sdu3MpoC}V*Z$TD#I576j+X=7 zMxcAS=gFm!rN@`IpZqNF?Cn}lj}houjvrZSU-B(;&wLg*Zh^z(y5I@LgBrR=?BI#1 z-{Za3J%+B0DMEhe&>RC0rANpqutGJ?Gsq^*po`BzX!aZgCTYiM$8!+o`w2%Br{Z`% z0QY($D`-Q&Q@5QQe3O6x27gYTO|rYl6zy0=(gY~DrGg>STOV~ zR5wKutm)EKU69SDD%yg5nC`q~4Q8N9%V2D??yZ2wIt2gsh9KJsBEq;8#0gQC+QJhQ zXH5~7Kp~dnnTzC#izCmq*+MNi9&^GQ+i4jtMO>f!2;W?eK#o`u@gaTfu{t9Fom2at8sgI(FoETck(;|7efQ<1(ZBb--|}7y zG``btD?bZ%Gr#b4((#ZQKo`In62FMZf+^dDUg>{#xdg8X*x z?sD%TqxTThzYg`@?u=D-^i*P96>jG@K37}wDscsy?>CcZr_nKD?0R&iV`OF9$YRs2 zaAdh{&q{djq6>n6Xjdh+s}k*k*JkD5V@th%O1wK}bSHsBxCuD;n!v3;!<=N+2;bf9 zA@ONOLx=fxVE9S)_io4l{?G?n$Y=wq^{PgBCz#1Z8%3XHHf1tJHCK}H`&uBAdATIW z)vs-#41A)anl4L$~3$liaE=Km1x3)20q`y9hG-6fb? Gd;Tx+TDZUf literal 0 HcmV?d00001 diff --git a/src/aigpt/ai_provider.py b/src/aigpt/ai_provider.py index 59575cd..a2154b2 100644 --- a/src/aigpt/ai_provider.py +++ b/src/aigpt/ai_provider.py @@ -102,7 +102,7 @@ class OpenAIProvider: config = Config() self.api_key = api_key or config.get_api_key("openai") or os.getenv("OPENAI_API_KEY") if not self.api_key: - raise ValueError("OpenAI API key not provided. Set it with: ai-gpt config set providers.openai.api_key YOUR_KEY") + raise ValueError("OpenAI API key not provided. Set it with: aigpt config set providers.openai.api_key YOUR_KEY") self.client = OpenAI(api_key=self.api_key) self.logger = logging.getLogger(__name__) @@ -169,4 +169,4 @@ def create_ai_provider(provider: str, model: str, **kwargs) -> AIProvider: elif provider == "openai": return OpenAIProvider(model=model, **kwargs) else: - raise ValueError(f"Unknown provider: {provider}") \ No newline at end of file + raise ValueError(f"Unknown provider: {provider}") diff --git a/src/aigpt/card_integration.py b/src/aigpt/card_integration.py new file mode 100644 index 0000000..b614767 --- /dev/null +++ b/src/aigpt/card_integration.py @@ -0,0 +1,150 @@ +"""ai.card integration module for ai.gpt MCP server""" + +from typing import Dict, Any, List, Optional +import httpx +from pathlib import Path +import json +from datetime import datetime +import logging + +logger = logging.getLogger(__name__) + + +class CardIntegration: + """Integration with ai.card system""" + + def __init__(self, api_base_url: str = "http://localhost:8001"): + self.api_base_url = api_base_url + self.client = httpx.AsyncClient() + + async def get_user_cards(self, did: str) -> List[Dict[str, Any]]: + """Get cards for a specific user by DID""" + try: + response = await self.client.get( + f"{self.api_base_url}/api/v1/cards/user/{did}" + ) + if response.status_code == 200: + return response.json() + else: + logger.error(f"Failed to get cards: {response.status_code}") + return [] + except Exception as e: + logger.error(f"Error getting user cards: {e}") + return [] + + async def draw_card(self, did: str) -> Optional[Dict[str, Any]]: + """Draw a new card for user (gacha)""" + try: + response = await self.client.post( + f"{self.api_base_url}/api/v1/gacha/draw", + json={"did": did} + ) + if response.status_code == 200: + return response.json() + else: + logger.error(f"Failed to draw card: {response.status_code}") + return None + except Exception as e: + logger.error(f"Error drawing card: {e}") + return None + + async def get_card_info(self, card_id: int) -> Optional[Dict[str, Any]]: + """Get detailed information about a specific card""" + try: + response = await self.client.get( + f"{self.api_base_url}/api/v1/cards/{card_id}" + ) + if response.status_code == 200: + return response.json() + else: + return None + except Exception as e: + logger.error(f"Error getting card info: {e}") + return None + + async def sync_with_atproto(self, did: str) -> bool: + """Sync card data with atproto""" + try: + response = await self.client.post( + f"{self.api_base_url}/api/v1/sync/atproto", + json={"did": did} + ) + return response.status_code == 200 + except Exception as e: + logger.error(f"Error syncing with atproto: {e}") + return False + + async def close(self): + """Close the HTTP client""" + await self.client.aclose() + + +def register_card_tools(app, card_integration: CardIntegration): + """Register ai.card tools to FastAPI app""" + + @app.get("/get_user_cards", operation_id="get_user_cards") + async def get_user_cards(did: str) -> List[Dict[str, Any]]: + """Get all cards owned by a user""" + cards = await card_integration.get_user_cards(did) + return cards + + @app.post("/draw_card", operation_id="draw_card") + async def draw_card(did: str) -> Dict[str, Any]: + """Draw a new card (gacha) for user""" + result = await card_integration.draw_card(did) + if result: + return { + "success": True, + "card": result + } + else: + return { + "success": False, + "error": "Failed to draw card" + } + + @app.get("/get_card_details", operation_id="get_card_details") + async def get_card_details(card_id: int) -> Dict[str, Any]: + """Get detailed information about a card""" + info = await card_integration.get_card_info(card_id) + if info: + return info + else: + return {"error": f"Card {card_id} not found"} + + @app.post("/sync_cards_atproto", operation_id="sync_cards_atproto") + async def sync_cards_atproto(did: str) -> Dict[str, str]: + """Sync user's cards with atproto""" + success = await card_integration.sync_with_atproto(did) + if success: + return {"status": "Cards synced successfully"} + else: + return {"status": "Failed to sync cards"} + + @app.get("/analyze_card_collection", operation_id="analyze_card_collection") + async def analyze_card_collection(did: str) -> Dict[str, Any]: + """Analyze user's card collection""" + cards = await card_integration.get_user_cards(did) + + if not cards: + return { + "total_cards": 0, + "rarity_distribution": {}, + "message": "No cards found" + } + + # Analyze collection + rarity_count = {} + total_power = 0 + + for card in cards: + rarity = card.get("rarity", "common") + rarity_count[rarity] = rarity_count.get(rarity, 0) + 1 + total_power += card.get("power", 0) + + return { + "total_cards": len(cards), + "rarity_distribution": rarity_count, + "average_power": total_power / len(cards) if cards else 0, + "strongest_card": max(cards, key=lambda x: x.get("power", 0)) if cards else None + } \ No newline at end of file diff --git a/src/aigpt/cli.py b/src/aigpt/cli.py index 06c8fdb..1378cce 100644 --- a/src/aigpt/cli.py +++ b/src/aigpt/cli.py @@ -9,7 +9,7 @@ from rich.panel import Panel from datetime import datetime, timedelta import subprocess import shlex -from prompt_toolkit import prompt +from prompt_toolkit import prompt as ptk_prompt from prompt_toolkit.completion import WordCompleter from prompt_toolkit.history import FileHistory from prompt_toolkit.auto_suggest import AutoSuggestFromHistory @@ -228,7 +228,8 @@ def server( port: int = typer.Option(8000, "--port", "-p", help="Server port"), data_dir: Optional[Path] = typer.Option(None, "--data-dir", "-d", help="Data directory"), model: str = typer.Option("qwen2.5", "--model", "-m", help="AI model to use"), - provider: str = typer.Option("ollama", "--provider", help="AI provider (ollama/openai)") + provider: str = typer.Option("ollama", "--provider", help="AI provider (ollama/openai)"), + enable_card: bool = typer.Option(False, "--enable-card", help="Enable ai.card integration") ): """Run MCP server for AI integration""" import uvicorn @@ -239,15 +240,16 @@ def server( data_dir.mkdir(parents=True, exist_ok=True) # Create MCP server - mcp_server = AIGptMcpServer(data_dir) - app_instance = mcp_server.get_server().get_app() + mcp_server = AIGptMcpServer(data_dir, enable_card_integration=enable_card) + app_instance = mcp_server.app console.print(Panel( f"[cyan]Starting ai.gpt MCP Server[/cyan]\n\n" f"Host: {host}:{port}\n" f"Provider: {provider}\n" f"Model: {model}\n" - f"Data: {data_dir}", + f"Data: {data_dir}\n" + f"Card Integration: {'✓ Enabled' if enable_card else '✗ Disabled'}", title="MCP Server", border_style="green" )) @@ -410,12 +412,22 @@ def shell( border_style="green" )) - # Command completer - commands = ['help', 'exit', 'quit', 'chat', 'status', 'clear', 'fortune', 'relationships'] - completer = WordCompleter(commands) + # Command completer with shell commands + builtin_commands = ['help', 'exit', 'quit', 'chat', 'status', 'clear', 'fortune', 'relationships', 'load'] + + # Add common shell commands + shell_commands = ['ls', 'cd', 'pwd', 'cat', 'echo', 'grep', 'find', 'mkdir', 'rm', 'cp', 'mv', + 'git', 'python', 'pip', 'npm', 'node', 'cargo', 'rustc', 'docker', 'kubectl'] + + # AI-specific commands + ai_commands = ['analyze', 'generate', 'explain', 'optimize', 'refactor', 'test', 'document'] + + all_commands = builtin_commands + ['!' + cmd for cmd in shell_commands] + ai_commands + completer = WordCompleter(all_commands, ignore_case=True) # History file - history_file = data_dir / "shell_history.txt" + actual_data_dir = data_dir if data_dir else DEFAULT_DATA_DIR + history_file = actual_data_dir / "shell_history.txt" history = FileHistory(str(history_file)) # Main shell loop @@ -424,7 +436,7 @@ def shell( while True: try: # Get input with completion - user_input = prompt( + user_input = ptk_prompt( "ai.shell> ", completer=completer, history=history, @@ -450,7 +462,12 @@ def shell( " status - Show AI status\n" " fortune - Check AI fortune\n" " relationships - List all relationships\n" - " clear - Clear the screen\n\n" + " clear - Clear the screen\n" + " load - Load aishell.md project file\n\n" + "[cyan]AI Commands:[/cyan]\n" + " analyze - Analyze a file with AI\n" + " generate - Generate code from description\n" + " explain - Get AI explanation\n\n" "You can also type any message to chat with AI\n" "Use Tab for command completion", title="Help", @@ -512,6 +529,68 @@ def shell( else: console.print("[yellow]No relationships yet[/yellow]") + # Load aishell.md command + elif user_input.lower() in ['load', 'load aishell.md', 'project']: + # Try to find and load aishell.md + search_paths = [ + Path.cwd() / "aishell.md", + Path.cwd() / "docs" / "aishell.md", + actual_data_dir.parent / "aishell.md", + Path.cwd() / "claude.md", # Also check for claude.md + ] + + loaded = False + for path in search_paths: + if path.exists(): + console.print(f"[cyan]Loading project file: {path}[/cyan]") + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + + # Process with AI to understand project + load_prompt = f"I've loaded the project specification. Please analyze it and understand the project goals:\n\n{content[:3000]}" + response, _ = persona.process_interaction(current_user, load_prompt, ai_provider) + console.print(f"\n[green]Project loaded successfully![/green]") + console.print(f"[cyan]AI Understanding:[/cyan]\n{response}") + loaded = True + break + + if not loaded: + console.print("[yellow]No aishell.md or claude.md found in project.[/yellow]") + console.print("Create aishell.md to define project goals and AI instructions.") + + # AI-powered commands + elif user_input.lower().startswith('analyze '): + # Analyze file or code + target = user_input[8:].strip() + if os.path.exists(target): + console.print(f"[cyan]Analyzing {target}...[/cyan]") + with open(target, 'r') as f: + content = f.read() + + analysis_prompt = f"Analyze this file and provide insights:\n\n{content[:2000]}" + response, _ = persona.process_interaction(current_user, analysis_prompt, ai_provider) + console.print(f"\n[cyan]Analysis:[/cyan]\n{response}") + else: + console.print(f"[red]File not found: {target}[/red]") + + elif user_input.lower().startswith('generate '): + # Generate code + gen_prompt = user_input[9:].strip() + if gen_prompt: + console.print("[cyan]Generating code...[/cyan]") + full_prompt = f"Generate code for: {gen_prompt}. Provide clean, well-commented code." + response, _ = persona.process_interaction(current_user, full_prompt, ai_provider) + console.print(f"\n[cyan]Generated Code:[/cyan]\n{response}") + + elif user_input.lower().startswith('explain '): + # Explain code or concept + topic = user_input[8:].strip() + if topic: + console.print(f"[cyan]Explaining {topic}...[/cyan]") + full_prompt = f"Explain this in detail: {topic}" + response, _ = persona.process_interaction(current_user, full_prompt, ai_provider) + console.print(f"\n[cyan]Explanation:[/cyan]\n{response}") + # Chat command or direct message else: # Remove 'chat' prefix if present diff --git a/src/aigpt/mcp_server.py b/src/aigpt/mcp_server.py index 7b999fa..bea87fc 100644 --- a/src/aigpt/mcp_server.py +++ b/src/aigpt/mcp_server.py @@ -1,12 +1,18 @@ """MCP Server for ai.gpt system""" from typing import Optional, List, Dict, Any -from fastapi_mcp import FastapiMcpServer +from fastapi_mcp import FastApiMCP +from fastapi import FastAPI from pathlib import Path import logging +import subprocess +import os +import shlex +from .ai_provider import create_ai_provider from .persona import Persona from .models import Memory, Relationship, PersonaState +from .card_integration import CardIntegration, register_card_tools logger = logging.getLogger(__name__) @@ -14,16 +20,29 @@ logger = logging.getLogger(__name__) class AIGptMcpServer: """MCP Server that exposes ai.gpt functionality to AI assistants""" - def __init__(self, data_dir: Path): + def __init__(self, data_dir: Path, enable_card_integration: bool = False): self.data_dir = data_dir self.persona = Persona(data_dir) - self.server = FastapiMcpServer("ai-gpt", "AI.GPT Memory and Relationship System") + + # Create FastAPI app + self.app = FastAPI( + title="AI.GPT Memory and Relationship System", + description="MCP server for ai.gpt system" + ) + + # Create MCP server with FastAPI app + self.server = FastApiMCP(self.app) + self.card_integration = None + + if enable_card_integration: + self.card_integration = CardIntegration() + self._register_tools() def _register_tools(self): """Register all MCP tools""" - @self.server.tool("get_memories") + @self.app.get("/get_memories", operation_id="get_memories") async def get_memories(user_id: Optional[str] = None, limit: int = 10) -> List[Dict[str, Any]]: """Get active memories from the AI's memory system""" memories = self.persona.memory.get_active_memories(limit=limit) @@ -39,7 +58,7 @@ class AIGptMcpServer: for mem in memories ] - @self.server.tool("get_relationship") + @self.app.get("/get_relationship", operation_id="get_relationship") async def get_relationship(user_id: str) -> Dict[str, Any]: """Get relationship status with a specific user""" rel = self.persona.relationships.get_or_create_relationship(user_id) @@ -53,7 +72,7 @@ class AIGptMcpServer: "last_interaction": rel.last_interaction.isoformat() if rel.last_interaction else None } - @self.server.tool("get_all_relationships") + @self.app.get("/get_all_relationships", operation_id="get_all_relationships") async def get_all_relationships() -> List[Dict[str, Any]]: """Get all relationships""" relationships = [] @@ -67,7 +86,7 @@ class AIGptMcpServer: }) return relationships - @self.server.tool("get_persona_state") + @self.app.get("/get_persona_state", operation_id="get_persona_state") async def get_persona_state() -> Dict[str, Any]: """Get current persona state including fortune and mood""" state = self.persona.get_current_state() @@ -82,7 +101,7 @@ class AIGptMcpServer: "active_memory_count": len(state.active_memories) } - @self.server.tool("process_interaction") + @self.app.post("/process_interaction", operation_id="process_interaction") async def process_interaction(user_id: str, message: str) -> Dict[str, Any]: """Process an interaction with a user""" response, relationship_delta = self.persona.process_interaction(user_id, message) @@ -96,7 +115,7 @@ class AIGptMcpServer: "relationship_status": rel.status.value } - @self.server.tool("check_transmission_eligibility") + @self.app.get("/check_transmission_eligibility", operation_id="check_transmission_eligibility") async def check_transmission_eligibility(user_id: str) -> Dict[str, Any]: """Check if AI can transmit to a specific user""" can_transmit = self.persona.can_transmit_to(user_id) @@ -110,7 +129,7 @@ class AIGptMcpServer: "transmission_enabled": rel.transmission_enabled } - @self.server.tool("get_fortune") + @self.app.get("/get_fortune", operation_id="get_fortune") async def get_fortune() -> Dict[str, Any]: """Get today's AI fortune""" fortune = self.persona.fortune_system.get_today_fortune() @@ -125,7 +144,7 @@ class AIGptMcpServer: "personality_modifiers": modifiers } - @self.server.tool("summarize_memories") + @self.app.post("/summarize_memories", operation_id="summarize_memories") async def summarize_memories(user_id: str) -> Optional[Dict[str, Any]]: """Create a summary of recent memories for a user""" summary = self.persona.memory.summarize_memories(user_id) @@ -138,12 +157,162 @@ class AIGptMcpServer: } return None - @self.server.tool("run_maintenance") + @self.app.post("/run_maintenance", operation_id="run_maintenance") async def run_maintenance() -> Dict[str, str]: """Run daily maintenance tasks""" self.persona.daily_maintenance() return {"status": "Maintenance completed successfully"} + + # Shell integration tools (ai.shell) + @self.app.post("/execute_command", operation_id="execute_command") + async def execute_command(command: str, working_dir: str = ".") -> Dict[str, Any]: + """Execute a shell command""" + try: + result = subprocess.run( + shlex.split(command), + cwd=working_dir, + capture_output=True, + text=True, + timeout=60 + ) + + return { + "status": "success" if result.returncode == 0 else "error", + "returncode": result.returncode, + "stdout": result.stdout, + "stderr": result.stderr, + "command": command + } + except subprocess.TimeoutExpired: + return {"error": "Command timed out"} + except Exception as e: + return {"error": str(e)} + + @self.app.post("/analyze_file", operation_id="analyze_file") + async def analyze_file(file_path: str, analysis_prompt: str = "Analyze this file") -> Dict[str, Any]: + """Analyze a file using AI""" + try: + if not os.path.exists(file_path): + return {"error": f"File not found: {file_path}"} + + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Get AI provider from app state + ai_provider = getattr(self.app.state, 'ai_provider', 'ollama') + ai_model = getattr(self.app.state, 'ai_model', 'qwen2.5') + + provider = create_ai_provider(ai_provider, ai_model) + + # Analyze with AI + prompt = f"{analysis_prompt}\n\nFile: {file_path}\n\nContent:\n{content}" + analysis = provider.generate_response(prompt, "You are a code analyst.") + + return { + "analysis": analysis, + "file_path": file_path, + "file_size": len(content), + "line_count": len(content.split('\n')) + } + except Exception as e: + return {"error": str(e)} + + @self.app.post("/write_file", operation_id="write_file") + async def write_file(file_path: str, content: str, backup: bool = True) -> Dict[str, Any]: + """Write content to a file""" + try: + file_path_obj = Path(file_path) + + # Create backup if requested + backup_path = None + if backup and file_path_obj.exists(): + backup_path = f"{file_path}.backup" + with open(file_path, 'r', encoding='utf-8') as src: + with open(backup_path, 'w', encoding='utf-8') as dst: + dst.write(src.read()) + + # Write file + file_path_obj.parent.mkdir(parents=True, exist_ok=True) + with open(file_path, 'w', encoding='utf-8') as f: + f.write(content) + + return { + "status": "success", + "file_path": file_path, + "backup_path": backup_path, + "bytes_written": len(content.encode('utf-8')) + } + except Exception as e: + return {"error": str(e)} + + @self.app.get("/read_project_file", operation_id="read_project_file") + async def read_project_file(file_name: str = "aishell.md") -> Dict[str, Any]: + """Read project files like aishell.md (similar to claude.md)""" + try: + # Check common locations + search_paths = [ + Path.cwd() / file_name, + Path.cwd() / "docs" / file_name, + self.data_dir.parent / file_name, + ] + + for path in search_paths: + if path.exists(): + with open(path, 'r', encoding='utf-8') as f: + content = f.read() + return { + "content": content, + "path": str(path), + "exists": True + } + + return { + "exists": False, + "searched_paths": [str(p) for p in search_paths], + "error": f"{file_name} not found" + } + except Exception as e: + return {"error": str(e)} + + @self.app.get("/list_files", operation_id="list_files") + async def list_files(directory: str = ".", pattern: str = "*") -> Dict[str, Any]: + """List files in a directory""" + try: + dir_path = Path(directory) + if not dir_path.exists(): + return {"error": f"Directory not found: {directory}"} + + files = [] + for item in dir_path.glob(pattern): + files.append({ + "name": item.name, + "path": str(item), + "is_file": item.is_file(), + "is_dir": item.is_dir(), + "size": item.stat().st_size if item.is_file() else None + }) + + return { + "directory": directory, + "pattern": pattern, + "files": files, + "count": len(files) + } + except Exception as e: + return {"error": str(e)} + + # Register ai.card tools if integration is enabled + if self.card_integration: + register_card_tools(self.app, self.card_integration) + + # Mount MCP server + self.server.mount() - def get_server(self) -> FastapiMcpServer: + def get_server(self) -> FastApiMCP: """Get the FastAPI MCP server instance""" - return self.server \ No newline at end of file + return self.server + + async def close(self): + """Cleanup resources""" + if self.card_integration: + await self.card_integration.close() \ No newline at end of file diff --git a/src/aigpt/mcp_server_simple.py b/src/aigpt/mcp_server_simple.py new file mode 100644 index 0000000..4215b2b --- /dev/null +++ b/src/aigpt/mcp_server_simple.py @@ -0,0 +1,146 @@ +"""Simple MCP Server implementation for ai.gpt""" + +from mcp import Server +from mcp.types import Tool, TextContent +from pathlib import Path +from typing import Any, Dict, List, Optional +import json + +from .persona import Persona +from .ai_provider import create_ai_provider +import subprocess +import os + + +def create_mcp_server(data_dir: Path, enable_card: bool = False) -> Server: + """Create MCP server with ai.gpt tools""" + server = Server("aigpt") + persona = Persona(data_dir) + + @server.tool() + async def get_memories(limit: int = 10) -> List[Dict[str, Any]]: + """Get active memories from the AI's memory system""" + memories = persona.memory.get_active_memories(limit=limit) + return [ + { + "id": mem.id, + "content": mem.content, + "level": mem.level.value, + "importance": mem.importance_score, + "is_core": mem.is_core, + "timestamp": mem.timestamp.isoformat() + } + for mem in memories + ] + + @server.tool() + async def get_relationship(user_id: str) -> Dict[str, Any]: + """Get relationship status with a specific user""" + rel = persona.relationships.get_or_create_relationship(user_id) + return { + "user_id": rel.user_id, + "status": rel.status.value, + "score": rel.score, + "transmission_enabled": rel.transmission_enabled, + "is_broken": rel.is_broken, + "total_interactions": rel.total_interactions, + "last_interaction": rel.last_interaction.isoformat() if rel.last_interaction else None + } + + @server.tool() + async def process_interaction(user_id: str, message: str, provider: str = "ollama", model: str = "qwen2.5") -> Dict[str, Any]: + """Process an interaction with a user""" + ai_provider = create_ai_provider(provider, model) + response, relationship_delta = persona.process_interaction(user_id, message, ai_provider) + rel = persona.relationships.get_or_create_relationship(user_id) + + return { + "response": response, + "relationship_delta": relationship_delta, + "new_relationship_score": rel.score, + "transmission_enabled": rel.transmission_enabled, + "relationship_status": rel.status.value + } + + @server.tool() + async def get_fortune() -> Dict[str, Any]: + """Get today's AI fortune""" + fortune = persona.fortune_system.get_today_fortune() + modifiers = persona.fortune_system.get_personality_modifier(fortune) + + return { + "value": fortune.fortune_value, + "date": fortune.date.isoformat(), + "consecutive_good": fortune.consecutive_good, + "consecutive_bad": fortune.consecutive_bad, + "breakthrough": fortune.breakthrough_triggered, + "personality_modifiers": modifiers + } + + @server.tool() + async def execute_command(command: str, working_dir: str = ".") -> Dict[str, Any]: + """Execute a shell command""" + try: + import shlex + result = subprocess.run( + shlex.split(command), + cwd=working_dir, + capture_output=True, + text=True, + timeout=60 + ) + + return { + "status": "success" if result.returncode == 0 else "error", + "returncode": result.returncode, + "stdout": result.stdout, + "stderr": result.stderr, + "command": command + } + except subprocess.TimeoutExpired: + return {"error": "Command timed out"} + except Exception as e: + return {"error": str(e)} + + @server.tool() + async def analyze_file(file_path: str) -> Dict[str, Any]: + """Analyze a file using AI""" + try: + if not os.path.exists(file_path): + return {"error": f"File not found: {file_path}"} + + with open(file_path, 'r', encoding='utf-8') as f: + content = f.read() + + ai_provider = create_ai_provider("ollama", "qwen2.5") + + prompt = f"Analyze this file and provide insights:\\n\\nFile: {file_path}\\n\\nContent:\\n{content[:2000]}" + analysis = ai_provider.generate_response(prompt, "You are a code analyst.") + + return { + "analysis": analysis, + "file_path": file_path, + "file_size": len(content), + "line_count": len(content.split('\\n')) + } + except Exception as e: + return {"error": str(e)} + + return server + + +async def main(): + """Run MCP server""" + import sys + from mcp import stdio_server + + data_dir = Path.home() / ".config" / "syui" / "ai" / "gpt" / "data" + data_dir.mkdir(parents=True, exist_ok=True) + + server = create_mcp_server(data_dir) + await stdio_server(server) + + +if __name__ == "__main__": + import asyncio + asyncio.run(main()) \ No newline at end of file diff --git a/src/aigpt/models.py b/src/aigpt/models.py index 7cf666b..13398b2 100644 --- a/src/aigpt/models.py +++ b/src/aigpt/models.py @@ -1,6 +1,6 @@ """Data models for ai.gpt system""" -from datetime import datetime +from datetime import datetime, date from typing import Optional, Dict, List, Any from enum import Enum from pydantic import BaseModel, Field @@ -52,7 +52,7 @@ class Relationship(BaseModel): class AIFortune(BaseModel): """Daily AI fortune affecting personality""" - date: datetime.date + date: date fortune_value: int = Field(ge=1, le=10) consecutive_good: int = 0 consecutive_bad: int = 0