amamiyax17 commited on
Commit
ec79641
·
0 Parent(s):

Initial commit with OpenRouter/Tavily updates and app.py path fix

Browse files
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # システムの依存関係をインストール
6
+ RUN apt-get update && apt-get install -y \
7
+ git \
8
+ build-essential \
9
+ curl \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ # Pythonの依存関係をコピーしてインストール
13
+ COPY requirements.txt .
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
+
16
+ # プロジェクトファイルをコピー
17
+ COPY . .
18
+
19
+ # Streamlitのポート設定
20
+ EXPOSE 7860
21
+
22
+ # ヘルスチェック
23
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
24
+ CMD curl -f http://localhost:7860/_stcore/health || exit 1
25
+
26
+ # アプリケーション起動
27
+ CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
app.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import sys
4
+ import streamlit as st
5
+ import subprocess
6
+ from pathlib import Path
7
+
8
+ # プロジェクトルートをパスに追加
9
+ project_root = Path(__file__).parent
10
+ sys.path.insert(0, str(project_root))
11
+ sys.path.insert(0, str(project_root / "src")) # src ディレクトリをPythonのパスに追加
12
+
13
+ def check_environment():
14
+ """環境設定をチェック"""
15
+ st.sidebar.markdown("### 🔧 システム状態")
16
+
17
+ # API キーの確認
18
+ openai_key = os.getenv('OPENAI_API_KEY')
19
+ tavily_key = os.getenv('TAVILY_API_KEY')
20
+
21
+ st.sidebar.markdown(f"**OpenRouter API**: {'✅' if openai_key else '❌'}")
22
+ st.sidebar.markdown(f"**Tavily API**: {'✅' if tavily_key else '❌'}")
23
+
24
+ # Python バージョン
25
+ python_version = sys.version.split()[0]
26
+ st.sidebar.markdown(f"**Python**: {python_version}")
27
+
28
+ # パッケージの確認
29
+ try:
30
+ import streamlit
31
+ st.sidebar.markdown(f"**Streamlit**: {streamlit.__version__}")
32
+ except ImportError:
33
+ st.sidebar.markdown("**Streamlit**: ❌")
34
+
35
+ return openai_key, tavily_key
36
+
37
+ def main():
38
+ st.set_page_config(
39
+ page_title="Lawsy - 法令Deep Research",
40
+ page_icon="⚖️",
41
+ layout="wide"
42
+ )
43
+
44
+ # 環境チェック
45
+ openai_key, tavily_key = check_environment()
46
+
47
+ # 必要な環境変数のチェック
48
+ if not openai_key:
49
+ st.error("⚠️ OPENAI_API_KEY(OpenRouter)が設定されていません")
50
+ st.info("Hugging Face Spaces の Settings → Variables and secrets で設定してください")
51
+ st.stop()
52
+
53
+ if not tavily_key:
54
+ st.error("⚠️ TAVILY_API_KEY が設定されていません")
55
+ st.info("Hugging Face Spaces の Settings → Variables and secrets で設定してください")
56
+ st.stop()
57
+
58
+ # 環境変数を設定
59
+ os.environ.setdefault('LAWSY_LM_PROVIDER', 'openrouter')
60
+ os.environ.setdefault('LAWSY_WEB_SEARCH_ENGINE', 'Tavily')
61
+
62
+ # メインアプリケーションを起動
63
+ try:
64
+ # まず、lawsyモジュールが存在するかチェック
65
+ if (Path(__file__).parent / "src" / "lawsy").exists():
66
+ st.success("✅ lawsyプロジェクトが見つかりました")
67
+
68
+ # 利用可能なモジュールを確認
69
+ lawsy_path = Path(__file__).parent / "lawsy"
70
+ python_files = list(lawsy_path.glob("**/*.py"))
71
+
72
+ st.sidebar.markdown("### 📁 プロジェクト構造")
73
+ for py_file in python_files[:10]: # 最初の10ファイルのみ表示
74
+ relative_path = py_file.relative_to(lawsy_path)
75
+ st.sidebar.markdown(f"- {relative_path}")
76
+
77
+ if len(python_files) > 10:
78
+ st.sidebar.markdown(f"... 他 {len(python_files) - 10} ファイル")
79
+
80
+ # デモインターフェース
81
+ st.title("⚖️ Lawsy - 法令Deep Research")
82
+ st.markdown("OpenRouter (shisa-ai/shisa-v2-llama3.3-70b:free) + Tavily API を使用")
83
+
84
+ # 簡単なテストインターフェース
85
+ query = st.text_input("法令に関する質問を入力してください:", "民法第1条について教えてください")
86
+
87
+ if st.button("検索・分析実行"):
88
+ with st.spinner("処理中..."):
89
+ try:
90
+ # ここで実際のlawsy機能を呼び出す
91
+ st.info("🔄 OpenRouterとTavily APIを使用してリサーチを実行しています...")
92
+
93
+ # Tavily検索のテスト
94
+ from lawsy.search.tavily_search import TavilySearch
95
+ search_engine = TavilySearch()
96
+ search_results = search_engine.search(query, max_results=3)
97
+
98
+ st.subheader("🔍 検索結果")
99
+ for i, result in enumerate(search_results, 1):
100
+ with st.expander(f"{i}. {result['title']}"):
101
+ st.write(f"**URL**: {result['url']}")
102
+ st.write(f"**内容**: {result['content'][:300]}...")
103
+ st.write(f"**スコア**: {result['score']}")
104
+
105
+ # OpenRouter LLMのテスト
106
+ from lawsy.providers.openrouter import OpenRouterClient
107
+ llm_client = OpenRouterClient()
108
+
109
+ # 検索結果を含むプロンプトを作成
110
+ context = "\n".join([f"- {r['title']}: {r['content'][:200]}..." for r in search_results])
111
+ messages = [
112
+ {"role": "system", "content": "あなたは法令の専門家です。検索結果を参考に、詳細で正確な回答を提供してください。"},
113
+ {"role": "user", "content": f"質問: {query}\n\n検索結果:\n{context}\n\n上記の情報を踏まえて回答してください。"}
114
+ ]
115
+
116
+ response = llm_client.chat_completion(messages)
117
+
118
+ st.subheader("🤖 AI分析結果")
119
+ st.write(response['choices'][0]['message']['content'])
120
+
121
+ except Exception as e:
122
+ st.error(f"❌ 処理中にエラーが発生しました: {str(e)}")
123
+ st.exception(e)
124
+
125
+ else:
126
+ st.error("❌ lawsyプロジェクトが見つかりません")
127
+ st.info("プロジェクトの構造を確認してください")
128
+
129
+ except Exception as e:
130
+ st.error(f"❌ アプリケーションの初期化に失敗しました: {str(e)}")
131
+ st.exception(e)
132
+
133
+ if __name__ == "__main__":
134
+ main()
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit>=1.28.0
2
+ streamlit-chat
3
+ streamlit-aggrid
4
+ requests>=2.31.0
5
+ openai>=1.0.0
6
+ python-dotenv
7
+ pandas
8
+ numpy
9
+ matplotlib
10
+ plotly
11
+ altair
12
+ beautifulsoup4
13
+ lxml
14
+ pydantic
15
+ fastapi
16
+ uvicorn
src/lawsy/providers/__init__.py ADDED
File without changes
src/lawsy/providers/openrouter.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # lawsy/providers/openrouter.py
2
+ import os
3
+ import requests
4
+ from typing import Dict, Any, Iterator
5
+
6
+ class OpenRouterClient:
7
+ def __init__(self):
8
+ self.api_key = os.getenv('OPENAI_API_KEY') # OpenRouterのキーを流用
9
+ self.base_url = "https://openrouter.ai/api/v1"
10
+ self.model = "shisa-ai/shisa-v2-llama3.3-70b:free"
11
+
12
+ def chat_completion(self, messages: list, **kwargs) -> Dict[str, Any]:
13
+ headers = {
14
+ "Authorization": f"Bearer {self.api_key}",
15
+ "Content-Type": "application/json"
16
+ }
17
+
18
+ data = {
19
+ "model": self.model,
20
+ "messages": messages,
21
+ **kwargs
22
+ }
23
+
24
+ response = requests.post(
25
+ f"{self.base_url}/chat/completions",
26
+ headers=headers,
27
+ json=data
28
+ )
29
+ response.raise_for_status()
30
+ return response.json()
31
+
32
+ def stream_completion(self, messages: list, **kwargs) -> Iterator[Dict[str, Any]]:
33
+ headers = {
34
+ "Authorization": f"Bearer {self.api_key}",
35
+ "Content-Type": "application/json"
36
+ }
37
+
38
+ data = {
39
+ "model": self.model,
40
+ "messages": messages,
41
+ "stream": True,
42
+ **kwargs
43
+ }
44
+
45
+ response = requests.post(
46
+ f"{self.base_url}/chat/completions",
47
+ headers=headers,
48
+ json=data,
49
+ stream=True
50
+ )
51
+ response.raise_for_status()
52
+
53
+ for line in response.iter_lines():
54
+ if line:
55
+ line = line.decode('utf-8')
56
+ if line.startswith('data: '):
57
+ data = line[6:]
58
+ if data.strip() == '[DONE]':
59
+ break
60
+ try:
61
+ import json
62
+ yield json.loads(data)
63
+ except json.JSONDecodeError:
64
+ continue
src/lawsy/search/__init__.py ADDED
File without changes
src/lawsy/search/tavily_search.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # lawsy/search/tavily_search.py
2
+ import os
3
+ import requests
4
+ from typing import List, Dict, Any
5
+
6
+ class TavilySearch:
7
+ def __init__(self):
8
+ self.api_key = os.getenv('TAVILY_API_KEY')
9
+ self.base_url = "https://api.tavily.com"
10
+
11
+ def search(self, query: str, max_results: int = 5) -> List[Dict[str, Any]]:
12
+ if not self.api_key:
13
+ raise ValueError("TAVILY_API_KEY environment variable is required")
14
+
15
+ headers = {
16
+ "Content-Type": "application/json"
17
+ }
18
+
19
+ data = {
20
+ "api_key": self.api_key,
21
+ "query": query,
22
+ "search_depth": "basic",
23
+ "include_answer": True,
24
+ "include_images": False,
25
+ "include_raw_content": False,
26
+ "max_results": max_results
27
+ }
28
+
29
+ response = requests.post(
30
+ f"{self.base_url}/search",
31
+ headers=headers,
32
+ json=data
33
+ )
34
+ response.raise_for_status()
35
+ result = response.json()
36
+
37
+ # 結果を標準化された形式に変換
38
+ formatted_results = []
39
+ for item in result.get('results', []):
40
+ formatted_results.append({
41
+ 'title': item.get('title', ''),
42
+ 'url': item.get('url', ''),
43
+ 'content': item.get('content', ''),
44
+ 'score': item.get('score', 0)
45
+ })
46
+
47
+ return formatted_results