Spaces:
Running
Running
| import streamlit as st | |
| import requests | |
| import json | |
| from duckduckgo_search import DDGS | |
| import re | |
| import clipboard | |
| import io | |
| import os | |
| import pandas as pd | |
| from PyPDF2 import PdfFileReader | |
| from PIL import Image | |
| import pytesseract | |
| from bs4 import BeautifulSoup | |
| import tiktoken | |
| from langchain import LLMChain, PromptTemplate | |
| # OpenRouter APIキーの設定 | |
| OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY").strip() | |
| # Streamlitアプリケーションの設定 | |
| st.set_page_config(page_title="Orb AI", page_icon="🤖", layout="wide") | |
| # セッション状態の初期化 | |
| if 'messages' not in st.session_state: | |
| st.session_state.messages = [] | |
| if 'uploaded_file_content' not in st.session_state: | |
| st.session_state.uploaded_file_content = None | |
| def read_pdf(file): | |
| pdf = PdfFileReader(file) | |
| content = "" | |
| for page_num in range(pdf.getNumPages()): | |
| page = pdf.getPage(page_num) | |
| content += page.extract_text() | |
| return content | |
| def read_csv(file): | |
| df = pd.read_csv(file) | |
| return df.to_string() | |
| def read_image(file): | |
| image = Image.open(file) | |
| text = pytesseract.image_to_string(image) | |
| return text | |
| def fetch_webpage_text(url): | |
| response = requests.get(url) | |
| if response.status_code != 200: | |
| return "Web page could not be retrieved" | |
| soup = BeautifulSoup(response.text, 'html.parser') | |
| paragraphs = soup.find_all('p') | |
| text_content = ' '.join([paragraph.get_text() for paragraph in paragraphs]) | |
| return text_content | |
| def truncate_prompt(prompt, max_tokens): | |
| tokenizer = tiktoken.get_encoding("cl100k_base") | |
| tokens = tokenizer.encode(prompt) | |
| if len(tokens) > max_tokens: | |
| truncated_tokens = tokens[:max_tokens] | |
| truncated_prompt = tokenizer.decode(truncated_tokens) | |
| return truncated_prompt | |
| return prompt | |
| def call_openrouter_api(prompt, model): | |
| max_context_tokens = 4096 | |
| completion_tokens = 500 | |
| max_prompt_tokens = max_context_tokens - completion_tokens | |
| truncated_prompt = truncate_prompt(prompt, max_prompt_tokens) | |
| response = requests.post( | |
| url="https://openrouter.ai/api/v1/chat/completions", | |
| headers={ | |
| "Authorization": f"Bearer {OPENROUTER_API_KEY}", | |
| "HTTP-Referer": "https://supertakerin2-comcomgptfree.hf.space/", | |
| "X-Title": "Orb AI", | |
| }, | |
| data=json.dumps({ | |
| "model": model, | |
| "messages": [{"role": "user", "content": truncated_prompt}] | |
| }) | |
| ) | |
| if response.status_code == 200: | |
| try: | |
| response_json = response.json() | |
| if "choices" in response_json: | |
| return response_json["choices"][0]["message"]["content"] | |
| else: | |
| st.error("API response does not contain 'choices'") | |
| return "API response does not contain 'choices'" | |
| except json.JSONDecodeError: | |
| st.error("API response JSON decoding failed.") | |
| return "API response JSON decoding failed." | |
| else: | |
| st.error(f"API request failed status code: {response.status_code}, message: {response.text}") | |
| return "API request failed" | |
| # サイドバーにAIモデルの選択、モード選択、ファイルアップロードを配置 | |
| with st.sidebar: | |
| ai_model = st.selectbox( | |
| "Select AI model", | |
| ["openchat/openchat-7b:free", "microsoft/phi-3-mini-128k-instruct:free", "meta-llama/llama-3.1-8b-instruct:free", "google/gemma-7b-it:free", "undi95/toppy-m-7b:free", "mistralai/mistral-7b-instruct:free"], | |
| key="model_select" | |
| ) | |
| mode = st.selectbox( | |
| "Select mode", | |
| ["focus", "writing", "video", "summary"], | |
| key="mode_select" | |
| ) | |
| # ファイルアップロード機能をサイドバーに移動 | |
| uploaded_file = st.file_uploader("Upload file", type=["txt", "pdf", "csv", "py", "html", "css", "js", "cs", "php", "java", "jpg"]) | |
| if uploaded_file is not None: | |
| file_extension = uploaded_file.name.split('.')[-1].lower() | |
| if file_extension in ['txt', 'py', 'html', 'css', 'js', 'cs', 'php', 'java']: | |
| stringio = io.StringIO(uploaded_file.getvalue().decode("utf-8")) | |
| st.session_state.uploaded_file_content = stringio.read() | |
| elif file_extension == 'pdf': | |
| st.session_state.uploaded_file_content = read_pdf(uploaded_file) | |
| elif file_extension == 'csv': | |
| st.session_state.uploaded_file_content = read_csv(uploaded_file) | |
| elif file_extension in ['jpg']: | |
| st.session_state.uploaded_file_content = read_image(uploaded_file) | |
| st.success(f"file '{uploaded_file.name}' was uploaded") | |
| # 会話をクリアするボタン | |
| if st.button("🔥 Delete conversation"): | |
| st.session_state.messages = [] | |
| st.session_state.uploaded_file_content = None | |
| # メインコンテンツ | |
| st.title("Orb AI") | |
| # チャット履歴の表示 | |
| for message in st.session_state.messages: | |
| with st.chat_message(message["role"]): | |
| st.markdown(message["content"]) | |
| # ユーザー入力 | |
| if prompt := st.chat_input("Please enter your message."): | |
| st.session_state.messages.append({"role": "user", "content": prompt}) | |
| with st.chat_message("user"): | |
| st.markdown(prompt) | |
| # アップロードされたファイルの内容を考慮してプロンプトを作成 | |
| if st.session_state.uploaded_file_content: | |
| prompt = f"以下のファイル内容を参考にして回答してください:\n\n{st.session_state.uploaded_file_content}\n\nユーザーの質問: {prompt}" | |
| full_response = "" | |
| if mode == "focus": | |
| # DuckDuckGo検索の実行 | |
| with DDGS() as ddgs: | |
| search_results = list(ddgs.text(prompt, max_results=4)) | |
| search_results_text = "\n".join([result["body"] for result in search_results]) | |
| urls = "\n".join([result["href"] for result in search_results[:4]]) | |
| prompt = f"質問: {prompt}\n\n検索結果: {search_results_text}\n\n上記の情報に基づいて、詳細な回答を提供してください:" | |
| full_response = call_openrouter_api(prompt, ai_model) | |
| # 回答の下に検索結果のURLを表示 | |
| with st.chat_message("assistant"): | |
| st.markdown(full_response) | |
| st.markdown("### Sources:") | |
| st.markdown(urls) | |
| elif mode == "writing": | |
| prompt = f"次のトピックについて包括的な記事を書いてください: {prompt}\n\n導入、主要なポイント、および結論を含めてください。" | |
| full_response = call_openrouter_api(prompt, ai_model) | |
| with st.chat_message("assistant"): | |
| st.markdown(full_response) | |
| elif mode == "summary": | |
| prompt = f"次のテキストを簡潔に要約してください:\n\n{prompt}\n\n主要なポイントを捉えた簡潔な要約を提供してください。" | |
| full_response = call_openrouter_api(prompt, ai_model) | |
| with st.chat_message("assistant"): | |
| st.markdown(full_response) | |
| elif mode == "video": | |
| # DuckDuckGo検索を使用してYouTubeの動画を検索 | |
| with DDGS() as ddgs: | |
| search_results = list(ddgs.text(f"{prompt} site:youtube.com", max_results=5)) | |
| if search_results: | |
| for result in search_results[:4]: | |
| video_url = result['href'] | |
| video_id = None | |
| youtube_patterns = [ | |
| r"(?<=v=)[^/]+", | |
| r"(?<=be/)[^/]+", | |
| r"(?<=embed/)[^/]+" | |
| ] | |
| for pattern in youtube_patterns: | |
| match = re.search(pattern, video_url) | |
| if match: | |
| video_id = match.group() | |
| break | |
| if video_id: | |
| st.markdown(f'<iframe width="560" height="315" src="https://www.youtube.com/embed/{video_id}" frameborder="0" allowfullscreen></iframe>', unsafe_allow_html=True) | |
| st.markdown(f"Related Videos: {result['title']}") | |
| else: | |
| st.warning(f"Video ID not found: {video_url}") | |
| else: | |
| full_response = "No related videos were found." | |
| with st.chat_message("assistant"): | |
| st.markdown(full_response) | |
| # コピー機能の追加 | |
| if st.button("📋 Copy the generated text"): | |
| clipboard.copy(full_response) | |
| st.success("Text has been copied to the clipboard!") |