franksoo commited on
Commit
09c1d64
ยท
1 Parent(s): caed852
Files changed (1) hide show
  1. app.py +40 -44
app.py CHANGED
@@ -6,14 +6,15 @@ import threading
6
  import httpx
7
  import logging
8
  import gradio as gr
 
 
9
  from openai import OpenAI
10
  from Crypto.Cipher import AES
11
  from Crypto.Util.Padding import unpad
12
- from fastapi import Request
13
- from fastapi.responses import PlainTextResponse
14
  import xml.etree.ElementTree as ET
 
15
 
16
- # โ”€โ”€ ๆ—ฅๅฟ—้…็ฝฎ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
17
  logging.basicConfig(
18
  level=logging.INFO,
19
  format="%(asctime)s [%(levelname)s] %(message)s",
@@ -29,11 +30,8 @@ WECOM_CORPID = os.getenv("CORPID", "").strip()
29
  WECOM_SECRET = os.getenv("WECOM_SECRET", "").strip()
30
  WECOM_AGENTID = os.getenv("WECOM_AGENTID", "1000003")
31
 
32
- # โ”€โ”€ LLM ๅฎขๆˆท็ซฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
33
- llm = OpenAI(
34
- base_url="https://integrate.api.nvidia.com/v1",
35
- api_key=NVIDIA_API_KEY,
36
- )
37
 
38
  SYSTEM_PROMPT = (
39
  "You are Hermes, a helpful AI assistant. "
@@ -60,7 +58,7 @@ def chat_with_hermes(message: str, history: list) -> str:
60
  log.info("[WEB] assistant: %s", reply)
61
  return reply
62
 
63
- # โ”€โ”€ ไผไธšๅพฎไฟกๅทฅๅ…ทๅ‡ฝๆ•ฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
64
  _wx_cache = {"token": "", "expires": 0}
65
  _lock = threading.Lock()
66
 
@@ -74,20 +72,20 @@ def get_wecom_token() -> str:
74
  params={"corpid": WECOM_CORPID, "corpsecret": WECOM_SECRET},
75
  timeout=10,
76
  ).json()
77
- log.info("[WECOM] get_token response: %s", r)
78
  _wx_cache["token"] = r.get("access_token", "")
79
  _wx_cache["expires"] = time.time() + r.get("expires_in", 7200)
80
  return _wx_cache["token"]
81
 
82
  def send_wecom_message(to_user: str, content: str):
83
  token = get_wecom_token()
84
- payload = {"touser": to_user, "msgtype": "text",
85
- "agentid": int(WECOM_AGENTID), "text": {"content": content}}
86
  r = httpx.post(
87
  f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}",
88
- json=payload, timeout=10,
 
 
89
  ).json()
90
- log.info("[WECOM] send_message to=%s result=%s", to_user, r)
91
 
92
  def decrypt_msg(encrypted: str) -> str:
93
  aes_key = base64.b64decode(WECOM_AES_KEY + "=")
@@ -100,42 +98,37 @@ def verify_sig(timestamp: str, nonce: str, encrypt: str = "") -> str:
100
  parts = [WECOM_TOKEN, timestamp, nonce] + ([encrypt] if encrypt else [])
101
  return hashlib.sha1("".join(sorted(parts)).encode()).hexdigest()
102
 
103
- # โ”€โ”€ Gradio ็•Œ้ข โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
104
- demo = gr.ChatInterface(
105
- fn=chat_with_hermes,
106
- title="๐Ÿค– Hermes Agent",
107
- description="Powered by NVIDIA NIM ยท ไผไธšๅพฎไฟกๅŒๆญฅๆŽฅๅ…ฅ",
108
- )
109
 
110
- # โ”€โ”€ ๆŒ‚่ฝฝไผไธšๅพฎไฟก่ทฏ็”ฑๅˆฐ Gradio ๅ†…็ฝฎ FastAPI app โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
111
- @demo.app.get("/gateway/wecom")
112
  async def wecom_verify(request: Request):
113
  p = request.query_params
114
- log.info("[WECOM] GET verify params: %s", dict(p))
115
  try:
116
- sig = verify_sig(p.get("timestamp",""), p.get("nonce",""), p.get("echostr",""))
117
- expected = p.get("msg_signature","")
118
- log.info("[WECOM] verify sig=%s expected=%s match=%s", sig, expected, sig==expected)
119
  if sig != expected:
120
  return PlainTextResponse("")
121
- plain = decrypt_msg(p.get("echostr",""))
122
- log.info("[WECOM] verify ok, echostr plain=%s", plain)
123
  return PlainTextResponse(plain)
124
  except Exception as e:
125
  log.exception("[WECOM] verify error: %s", e)
126
  return PlainTextResponse("")
127
 
128
- @demo.app.post("/gateway/wecom")
129
  async def wecom_message(request: Request):
130
  p = request.query_params
131
  log.info("[WECOM] POST params: %s", dict(p))
132
  try:
133
  body = await request.body()
134
- log.info("[WECOM] POST body: %s", body.decode())
135
  encrypt = ET.fromstring(body.decode()).findtext("Encrypt", "")
136
- sig = verify_sig(p.get("timestamp",""), p.get("nonce",""), encrypt)
137
- expected = p.get("msg_signature","")
138
- log.info("[WECOM] msg sig=%s expected=%s match=%s", sig, expected, sig==expected)
139
  if sig != expected:
140
  return PlainTextResponse("success")
141
  root = ET.fromstring(decrypt_msg(encrypt))
@@ -144,12 +137,12 @@ async def wecom_message(request: Request):
144
  log.info("[WECOM] msg_type=%s from=%s", msg_type, from_user)
145
  if msg_type == "text":
146
  content = root.findtext("Content", "").strip()
147
- log.info("[WECOM] user=%s msg=%s", from_user, content)
148
  if content:
149
  def reply():
150
  try:
151
  answer = chat_with_hermes(content, [])
152
- log.info("[WECOM] reply to=%s answer=%s", from_user, answer)
153
  send_wecom_message(from_user, answer)
154
  except Exception as e:
155
  log.exception("[WECOM] reply error: %s", e)
@@ -158,14 +151,17 @@ async def wecom_message(request: Request):
158
  log.exception("[WECOM] message error: %s", e)
159
  return PlainTextResponse("success")
160
 
 
 
 
 
 
 
 
 
161
  # โ”€โ”€ ๅฏๅŠจ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
162
  if __name__ == "__main__":
163
- log.info("Starting Hermes Agent...")
164
- log.info("WECOM_TOKEN set: %s", bool(WECOM_TOKEN))
165
- log.info("WECOM_AES_KEY set: %s", bool(WECOM_AES_KEY))
166
- log.info("WECOM_CORPID set: %s", bool(WECOM_CORPID))
167
- log.info("WECOM_SECRET set: %s", bool(WECOM_SECRET))
168
- log.info("NVIDIA_API_KEY set: %s", bool(NVIDIA_API_KEY))
169
- routes = [r.path for r in demo.app.routes]
170
- log.info("Registered routes: %s", routes)
171
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
6
  import httpx
7
  import logging
8
  import gradio as gr
9
+ from fastapi import FastAPI, Request
10
+ from fastapi.responses import PlainTextResponse
11
  from openai import OpenAI
12
  from Crypto.Cipher import AES
13
  from Crypto.Util.Padding import unpad
 
 
14
  import xml.etree.ElementTree as ET
15
+ import uvicorn
16
 
17
+ # โ”€โ”€ ๆ—ฅๅฟ— โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
18
  logging.basicConfig(
19
  level=logging.INFO,
20
  format="%(asctime)s [%(levelname)s] %(message)s",
 
30
  WECOM_SECRET = os.getenv("WECOM_SECRET", "").strip()
31
  WECOM_AGENTID = os.getenv("WECOM_AGENTID", "1000003")
32
 
33
+ # โ”€โ”€ LLM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
34
+ llm = OpenAI(base_url="https://integrate.api.nvidia.com/v1", api_key=NVIDIA_API_KEY)
 
 
 
35
 
36
  SYSTEM_PROMPT = (
37
  "You are Hermes, a helpful AI assistant. "
 
58
  log.info("[WEB] assistant: %s", reply)
59
  return reply
60
 
61
+ # โ”€โ”€ ไผไธšๅพฎไฟก โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
62
  _wx_cache = {"token": "", "expires": 0}
63
  _lock = threading.Lock()
64
 
 
72
  params={"corpid": WECOM_CORPID, "corpsecret": WECOM_SECRET},
73
  timeout=10,
74
  ).json()
75
+ log.info("[WECOM] get_token: %s", r)
76
  _wx_cache["token"] = r.get("access_token", "")
77
  _wx_cache["expires"] = time.time() + r.get("expires_in", 7200)
78
  return _wx_cache["token"]
79
 
80
  def send_wecom_message(to_user: str, content: str):
81
  token = get_wecom_token()
 
 
82
  r = httpx.post(
83
  f"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}",
84
+ json={"touser": to_user, "msgtype": "text",
85
+ "agentid": int(WECOM_AGENTID), "text": {"content": content}},
86
+ timeout=10,
87
  ).json()
88
+ log.info("[WECOM] send to=%s result=%s", to_user, r)
89
 
90
  def decrypt_msg(encrypted: str) -> str:
91
  aes_key = base64.b64decode(WECOM_AES_KEY + "=")
 
98
  parts = [WECOM_TOKEN, timestamp, nonce] + ([encrypt] if encrypt else [])
99
  return hashlib.sha1("".join(sorted(parts)).encode()).hexdigest()
100
 
101
+ # โ”€โ”€ FastAPI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
102
+ fastapi_app = FastAPI()
 
 
 
 
103
 
104
+ @fastapi_app.get("/gateway/wecom")
 
105
  async def wecom_verify(request: Request):
106
  p = request.query_params
107
+ log.info("[WECOM] GET params: %s", dict(p))
108
  try:
109
+ sig = verify_sig(p.get("timestamp", ""), p.get("nonce", ""), p.get("echostr", ""))
110
+ expected = p.get("msg_signature", "")
111
+ log.info("[WECOM] sig=%s expected=%s match=%s", sig, expected, sig == expected)
112
  if sig != expected:
113
  return PlainTextResponse("")
114
+ plain = decrypt_msg(p.get("echostr", ""))
115
+ log.info("[WECOM] verify ok plain=%s", plain)
116
  return PlainTextResponse(plain)
117
  except Exception as e:
118
  log.exception("[WECOM] verify error: %s", e)
119
  return PlainTextResponse("")
120
 
121
+ @fastapi_app.post("/gateway/wecom")
122
  async def wecom_message(request: Request):
123
  p = request.query_params
124
  log.info("[WECOM] POST params: %s", dict(p))
125
  try:
126
  body = await request.body()
127
+ log.info("[WECOM] body: %s", body.decode())
128
  encrypt = ET.fromstring(body.decode()).findtext("Encrypt", "")
129
+ sig = verify_sig(p.get("timestamp", ""), p.get("nonce", ""), encrypt)
130
+ expected = p.get("msg_signature", "")
131
+ log.info("[WECOM] sig=%s expected=%s match=%s", sig, expected, sig == expected)
132
  if sig != expected:
133
  return PlainTextResponse("success")
134
  root = ET.fromstring(decrypt_msg(encrypt))
 
137
  log.info("[WECOM] msg_type=%s from=%s", msg_type, from_user)
138
  if msg_type == "text":
139
  content = root.findtext("Content", "").strip()
140
+ log.info("[WECOM] content=%s", content)
141
  if content:
142
  def reply():
143
  try:
144
  answer = chat_with_hermes(content, [])
145
+ log.info("[WECOM] reply=%s", answer)
146
  send_wecom_message(from_user, answer)
147
  except Exception as e:
148
  log.exception("[WECOM] reply error: %s", e)
 
151
  log.exception("[WECOM] message error: %s", e)
152
  return PlainTextResponse("success")
153
 
154
+ # โ”€โ”€ Gradio ๆŒ‚่ฝฝๅˆฐ FastAPI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
155
+ demo = gr.ChatInterface(
156
+ fn=chat_with_hermes,
157
+ title="๐Ÿค– Hermes Agent",
158
+ description="Powered by NVIDIA NIM ยท ไผไธšๅพฎไฟกๅŒๆญฅๆŽฅๅ…ฅ",
159
+ )
160
+ app = gr.mount_gradio_app(fastapi_app, demo, path="/")
161
+
162
  # โ”€โ”€ ๅฏๅŠจ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
163
  if __name__ == "__main__":
164
+ log.info("WECOM_TOKEN=%s AES_KEY=%s CORPID=%s SECRET=%s NVIDIA=%s",
165
+ bool(WECOM_TOKEN), bool(WECOM_AES_KEY),
166
+ bool(WECOM_CORPID), bool(WECOM_SECRET), bool(NVIDIA_API_KEY))
167
+ uvicorn.run(app, host="0.0.0.0", port=7860)