1
- # MIT License
2
-
3
- # Copyright (c) 2024 starpig1129
4
-
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
-
12
- # The above copyright notice and this permission notice shall be included in all
13
- # copies or substantial portions of the Software.
14
-
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- # SOFTWARE.
22
- from anthropic import Anthropic , AsyncAnthropic , HUMAN_PROMPT , AI_PROMPT
23
- import requests
24
- import logging
25
1
import os
2
+ from anthropic import AsyncAnthropic , HUMAN_PROMPT , AI_PROMPT
3
+ from dotenv import load_dotenv
4
+ import asyncio
5
+ from threading import Thread
6
+ from queue import Queue
7
+
8
+ # 加載 .env 文件中的環境變量
9
+ load_dotenv ()
10
+
26
11
# 初始化 Anthropic 客戶端
27
- anthropic = Anthropic (api_key = os .getenv ("ANTHROPIC_API_KEY" ))
28
12
async_anthropic = AsyncAnthropic (api_key = os .getenv ("ANTHROPIC_API_KEY" ))
29
- # 檢查 Claude API 的額度
30
- def check_claude_api_quota ():
31
- ANTHROPIC_API_KEY = os .getenv ("ANTHROPIC_API_KEY" )
32
- if not ANTHROPIC_API_KEY :
33
- logging .warning ("找不到 Anthropic API 金鑰" )
34
- return None
35
-
36
- try :
37
- # 發送請求到 Anthropic 的額度檢查端點
38
- response = requests .get (
39
- "https://api.anthropic.com/v1/quota" ,
40
- headers = {"Authorization" : f"Bearer { ANTHROPIC_API_KEY } " }
41
- )
42
- response .raise_for_status ()
43
- quota_info = response .json ()
44
-
45
- # 返回剩餘額度信息
46
- return quota_info .get ("remaining_quota" )
47
- except Exception as e :
48
- logging .error (f"檢查 Claude API 額度時發生錯誤: { e } " )
49
- return None
50
- # 使用 Claude API 生成流式回應
51
- async def generate_claude_stream_response (system_prompt ,prompt , history , message_to_edit , channel ):
52
- try :
53
- # 準備對話歷史
54
- messages = []
55
- for msg in history :
13
+
14
+ class FakeThread :
15
+ def __init__ (self , target , * args , ** kwargs ):
16
+ self ._target = target
17
+ self ._args = args
18
+ self ._kwargs = kwargs
19
+ self .thread = Thread (target = self ._run )
20
+ self .is_finished = False
21
+
22
+ def _run (self ):
23
+ loop = asyncio .new_event_loop ()
24
+ asyncio .set_event_loop (loop )
25
+ loop .run_until_complete (self ._target (* self ._args , ** self ._kwargs ))
26
+ self .is_finished = True
27
+
28
+ def start (self ):
29
+ self .thread .start ()
30
+
31
+ def join (self ):
32
+ self .thread .join ()
33
+
34
+ class Streamer :
35
+ def __init__ (self ):
36
+ self .queue = Queue ()
37
+ self .is_finished = False
38
+
39
+ def write (self , content ):
40
+ self .queue .put (content )
41
+
42
+ def finish (self ):
43
+ self .is_finished = True
44
+ self .queue .put (None ) # 结束标记
45
+
46
+ def __iter__ (self ):
47
+ return self
48
+
49
+ def __next__ (self ):
50
+ item = self .queue .get ()
51
+ if item is None :
52
+ raise StopIteration
53
+ return item
54
+
55
+ async def generate_claude_response_with_fake_thread (inst , system_prompt , streamer , dialogue_history = None ):
56
+ messages = []
57
+ if dialogue_history :
58
+ for msg in dialogue_history :
56
59
role = HUMAN_PROMPT if msg ["role" ] == "user" else AI_PROMPT
57
60
messages .append (f"{ role } { msg ['content' ]} " )
58
-
59
- messages .append (f"{ HUMAN_PROMPT } { system_prompt } { prompt } " )
60
- full_prompt = "\n \n " .join (messages )
61
+
62
+ messages .append (f"{ HUMAN_PROMPT } { system_prompt } { inst } " )
63
+ full_prompt = "\n \n " .join (messages )
61
64
62
- # 使用 Claude API 生成流式回應
65
+ try :
63
66
async with async_anthropic as client :
64
67
response_stream = await client .completions .create (
65
68
model = "claude-3-5-sonnet-20240620" ,
@@ -68,35 +71,30 @@ async def generate_claude_stream_response(system_prompt,prompt, history, message
68
71
stream = True
69
72
)
70
73
71
- full_response = ""
72
- current_message = message_to_edit
73
- buffer = ""
74
- message_result = ""
75
- buffer_size = 40 # 設置緩衝區大小
76
-
77
74
async for completion in response_stream :
78
75
if completion .stop_reason :
79
76
break
80
77
chunk = completion .completion
81
- full_response += chunk
82
- buffer += chunk
83
- message_result += chunk
84
- if len (buffer ) >= buffer_size :
85
- # 檢查是否超過 1900 字符
86
- if len (full_response + buffer )> 1900 :
87
- # 創建新消息
88
- current_message = await channel .send ("繼續輸出中..." )
89
- full_response = ""
90
- await current_message .edit (content = full_response + buffer )
91
- buffer = "" # 清空緩衝區
92
-
93
- # 處理剩餘的文本
94
- if buffer :
95
- if len (full_response + buffer )> 1900 :
96
- current_message = await channel .send (buffer )
97
- else :
98
- await current_message .edit (content = full_response + buffer )
99
- return message_result
78
+ streamer .write (chunk )
100
79
except Exception as e :
101
- logging .error (f"使用 Claude API 生成回應時發生錯誤: { e } " )
102
- raise e
80
+ streamer .write (f"\n An error occurred: { str (e )} " )
81
+ finally :
82
+ streamer .finish ()
83
+
84
+ async def generate_claude_response (inst , system_prompt , dialogue_history = None ):
85
+ streamer = Streamer ()
86
+ fake_thread = FakeThread (generate_claude_response_with_fake_thread , inst , system_prompt , streamer , dialogue_history )
87
+ fake_thread .start ()
88
+ return fake_thread , streamer
89
+
90
+ # 使用示例
91
+ def main ():
92
+ thread , streamer = asyncio .run (generate_claude_response ("Hello, how are you?" , "You are a helpful assistant." ))
93
+
94
+ for content in streamer :
95
+ print (content , end = '' , flush = True )
96
+
97
+ thread .join ()
98
+
99
+ if __name__ == "__main__" :
100
+ main ()
0 commit comments