Files
pj_llama/llama_universal.py
aken1023 ece261ed8a 新增內網外網選擇功能
新增功能:
 llama_universal.py - 主程式支援內外網環境選擇
🌐 內網端點:http://192.168.0.6:21180-21182/v1
🌐 外網端點:https://llama.theaken.com/v1/*
🔄 對話中可使用 'switch' 指令切換環境
📱 智慧環境偵測和錯誤處理

更新內容:
- 新增網路環境選擇介面
- 支援內網三個端點 (21180, 21181, 21182)
- 支援外網三個端點(通用、GPT專用、DeepSeek專用)
- 新增 switch 指令可在對話中切換環境
- 完整的錯誤處理和重試機制
- 更新 README.md 說明新功能和使用方式

現在用戶可以根據所在網路環境自由選擇最適合的連接方式!
2025-09-19 23:27:19 +08:00

337 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Llama API 通用對話程式
支援內網和外網端點選擇
"""
from openai import OpenAI
import requests
import sys
import re
from datetime import datetime
# API 金鑰
API_KEY = "paVrIT+XU1NhwCAOb0X4aYi75QKogK5YNMGvQF1dCyo="
# 網路環境配置
NETWORK_CONFIG = {
"內網": {
"name": "內網環境",
"description": "公司/學校內部網路,使用內部 IP 地址",
"endpoints": [
{
"name": "內網端點 1",
"url": "http://192.168.0.6:21180/v1",
"models": ["gpt-oss-120b", "deepseek-r1-671b", "qwen3-embedding-8b"]
},
{
"name": "內網端點 2",
"url": "http://192.168.0.6:21181/v1",
"models": ["gpt-oss-120b", "deepseek-r1-671b", "qwen3-embedding-8b"]
},
{
"name": "內網端點 3",
"url": "http://192.168.0.6:21182/v1",
"models": ["gpt-oss-120b", "deepseek-r1-671b", "qwen3-embedding-8b"]
}
]
},
"外網": {
"name": "外網環境",
"description": "公開網際網路,使用外部域名",
"endpoints": [
{
"name": "通用端點",
"url": "https://llama.theaken.com/v1",
"models": ["gpt-oss-120b", "deepseek-r1-671b", "qwen3-embedding-8b"]
},
{
"name": "GPT-OSS 專用端點",
"url": "https://llama.theaken.com/v1/gpt-oss-120b",
"models": ["gpt-oss-120b"]
},
{
"name": "DeepSeek 專用端點",
"url": "https://llama.theaken.com/v1/deepseek-r1-671b",
"models": ["deepseek-r1-671b"]
}
]
}
}
def clean_response(text):
"""清理 AI 回應中的特殊標記"""
if not text:
return text
# 移除思考標記
if "<think>" in text:
text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
# 移除 channel 標記
if "<|channel|>" in text:
parts = text.split("<|message|>")
if len(parts) > 1:
text = parts[-1]
# 移除結束標記
text = text.replace("<|end|>", "").replace("<|start|>", "")
# 清理多餘空白
text = text.strip()
return text
def test_endpoint(endpoint_info, timeout=8):
"""測試端點是否可用"""
url = endpoint_info["url"]
model = endpoint_info["models"][0] if endpoint_info["models"] else "gpt-oss-120b"
print(f" 測試 {endpoint_info['name']}...", end="", flush=True)
try:
# 處理特殊的模型端點 URL
if url.endswith("/gpt-oss-120b") or url.endswith("/deepseek-r1-671b"):
base_url = url.rsplit("/", 1)[0]
else:
base_url = url
client = OpenAI(
api_key=API_KEY,
base_url=base_url,
timeout=timeout
)
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "test"}],
max_tokens=5
)
print(" [OK]")
return True
except Exception as e:
error_msg = str(e)
if "502" in error_msg:
print(" [502 - 伺服器離線]")
elif "timeout" in error_msg.lower():
print(" [超時]")
elif "connection" in error_msg.lower():
print(" [連接失敗]")
else:
print(" [錯誤]")
return False
def choose_network_environment():
"""選擇網路環境"""
print("\n" + "="*60)
print("選擇網路環境")
print("="*60)
print("\n可用環境:")
environments = list(NETWORK_CONFIG.keys())
for i, env in enumerate(environments, 1):
config = NETWORK_CONFIG[env]
print(f" {i}. {config['name']}")
print(f" 說明:{config['description']}")
print(f" 端點數量:{len(config['endpoints'])}")
while True:
try:
choice = input(f"\n請選擇環境 (1-{len(environments)}),預設為 1: ").strip()
if not choice:
choice = "1"
choice_num = int(choice)
if 1 <= choice_num <= len(environments):
selected_env = environments[choice_num - 1]
print(f"\n已選擇:{NETWORK_CONFIG[selected_env]['name']}")
return selected_env
else:
print(f"請輸入 1-{len(environments)} 之間的數字")
except ValueError:
print("請輸入有效的數字")
except KeyboardInterrupt:
print("\n\n程式已取消")
sys.exit(0)
def find_working_endpoint(environment):
"""尋找指定環境中的可用端點"""
config = NETWORK_CONFIG[environment]
print(f"\n正在測試 {config['name']} 的端點...")
print("-" * 50)
for endpoint in config['endpoints']:
if test_endpoint(endpoint):
return endpoint
return None
def chat_session(endpoint_info, environment):
"""對話主程式"""
print("\n" + "="*60)
print("Llama AI 對話系統")
print("="*60)
print(f"網路環境: {NETWORK_CONFIG[environment]['name']}")
print(f"使用端點: {endpoint_info['name']}")
print(f"URL: {endpoint_info['url']}")
print(f"可用模型: {', '.join(endpoint_info['models'])}")
print("\n指令:")
print(" exit/quit - 結束對話")
print(" clear - 清空對話歷史")
print(" model - 切換模型")
print(" switch - 切換網路環境")
print("-"*60)
# 處理 URL
url = endpoint_info["url"]
if url.endswith("/gpt-oss-120b") or url.endswith("/deepseek-r1-671b"):
base_url = url.rsplit("/", 1)[0]
else:
base_url = url
client = OpenAI(api_key=API_KEY, base_url=base_url)
# 選擇模型
if len(endpoint_info['models']) == 1:
current_model = endpoint_info['models'][0]
else:
print("\n選擇模型:")
for i, model in enumerate(endpoint_info['models'], 1):
print(f" {i}. {model}")
choice = input("選擇 (預設: 1): ").strip()
if choice.isdigit() and 1 <= int(choice) <= len(endpoint_info['models']):
current_model = endpoint_info['models'][int(choice)-1]
else:
current_model = endpoint_info['models'][0]
print(f"\n使用模型: {current_model}")
messages = []
while True:
try:
user_input = input("\n你: ").strip()
if not user_input:
continue
if user_input.lower() in ['exit', 'quit']:
print("再見!")
break
if user_input.lower() == 'clear':
messages = []
print("[系統] 對話歷史已清空")
continue
if user_input.lower() == 'switch':
print("[系統] 重新啟動程式以切換網路環境")
return "switch"
if user_input.lower() == 'model':
if len(endpoint_info['models']) == 1:
print(f"[系統] 此端點只支援 {endpoint_info['models'][0]}")
else:
print("\n可用模型:")
for i, m in enumerate(endpoint_info['models'], 1):
print(f" {i}. {m}")
choice = input("選擇: ").strip()
if choice.isdigit() and 1 <= int(choice) <= len(endpoint_info['models']):
current_model = endpoint_info['models'][int(choice)-1]
print(f"[系統] 已切換到 {current_model}")
continue
messages.append({"role": "user", "content": user_input})
print("\nAI 思考中...", end="", flush=True)
try:
response = client.chat.completions.create(
model=current_model,
messages=messages,
temperature=0.7,
max_tokens=1000
)
ai_response = response.choices[0].message.content
ai_response = clean_response(ai_response)
print("\r" + " "*20 + "\r", end="")
print(f"AI: {ai_response}")
messages.append({"role": "assistant", "content": ai_response})
except Exception as e:
print(f"\r[錯誤] {str(e)[:100]}")
messages.pop()
except KeyboardInterrupt:
print("\n\n[中斷] 使用 exit 命令正常退出")
continue
except EOFError:
print("\n再見!")
break
return "exit"
def main():
while True:
print("="*60)
print("Llama AI 通用對話程式")
print(f"時間: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("="*60)
# 選擇網路環境
environment = choose_network_environment()
# 尋找可用端點
working_endpoint = find_working_endpoint(environment)
if not working_endpoint:
print("\n" + "="*60)
print(f"錯誤:無法連接到任何 {NETWORK_CONFIG[environment]['name']} 端點")
print("="*60)
print("\n可能的原因:")
if environment == "內網":
print("1. 不在內網環境或 IP 設定錯誤")
print("2. 內網 API 服務未啟動")
print("3. 防火牆阻擋連接")
print("\n建議:嘗試選擇外網環境")
else:
print("1. 外網 API 伺服器暫時離線")
print("2. 網路連接問題")
print("3. 防火牆或代理設定")
print("\n建議:嘗試選擇內網環境或稍後再試")
retry = input("\n是否重新選擇環境?(y/n預設: y): ").strip().lower()
if retry in ['n', 'no']:
break
continue
print("\n" + "="*60)
print(f"成功連接到: {working_endpoint['name']}")
print("="*60)
# 開始對話
result = chat_session(working_endpoint, environment)
if result == "exit":
break
elif result == "switch":
continue # 重新開始選擇環境
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n程式已退出")
except Exception as e:
print(f"\n[錯誤] {e}")
import traceback
traceback.print_exc()
sys.exit(1)