lele commited on
Commit
8445a21
·
1 Parent(s): c7653ab

modified: .gitignore

Browse files

modified: README.md
modified: app.py
modified: prompt.txt
renamed: ai_transform.py -> src/ai_transform.py
renamed: utils.py -> src/utils.py
new file: templates/index.html

.gitignore CHANGED
@@ -1,5 +1,6 @@
1
  secret.env
2
  test*.py
 
3
  notion_data.json
4
  prompt_1.txt
5
  prompt_2.txt
 
1
  secret.env
2
  test*.py
3
+ test/
4
  notion_data.json
5
  prompt_1.txt
6
  prompt_2.txt
README.md CHANGED
@@ -7,10 +7,16 @@ sdk: docker
7
  pinned: false
8
  license: apache-2.0
9
  short_description: Push my data to web and transfer to Notion
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
13
 
 
 
 
 
 
14
  # update
15
  - gpt 3.5 to classify the project.
16
  - generalize the model usage.
@@ -23,6 +29,10 @@ curl -X POST https://beanundlaugh-notion-pusher.hf.space/ \
23
 
24
  content should be a string with form "content"
25
 
 
 
 
 
26
  # test locally
27
  ## get_all function
28
 
 
7
  pinned: false
8
  license: apache-2.0
9
  short_description: Push my data to web and transfer to Notion
10
+ version: 2.0.0
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
14
 
15
+ # update2
16
+ build a async frontend for visualization
17
+ set a debugging mode, which skips the entire Notion API interaction loop.(accept debug as a query)
18
+ reconstruct the modules.
19
+
20
  # update
21
  - gpt 3.5 to classify the project.
22
  - generalize the model usage.
 
29
 
30
  content should be a string with form "content"
31
 
32
+ or
33
+
34
+ visit the root and directly enter the content.
35
+
36
  # test locally
37
  ## get_all function
38
 
app.py CHANGED
@@ -1,34 +1,18 @@
1
- from flask import Flask, request, jsonify
2
- from ai_transform import gemini_get_answer, classify_task_with_ai
3
- from utils import *
4
  import requests, os, datetime
5
- import time
6
  import json
 
 
 
 
 
7
  app = Flask(__name__)
8
 
9
  NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
10
  DB_ID = os.environ.get("DB_ID")
11
- with open("prompt.txt", encoding="utf-8") as f:
12
- system_content = f.read()
13
 
14
- def time_cali():
15
- # 拿到 UTC 时间戳
16
- ts = time.time()
17
- # 手动加 8 小时
18
- beijing_ts = ts + 8*3600
19
- # 再转回 struct_time
20
- beijing_t = time.gmtime(beijing_ts)
21
- # print(time.strftime('%Y-%m-%d', beijing_t))
22
- return time.strftime('%Y-%m-%d', beijing_t)
23
- def modified_with_ai(items):
24
- # 在这里调用AI模型对item进行修改
25
- # today_date = time.strftime("%Y-%m-%d", time.localtime())
26
- today_date = time_cali()
27
- system_instu = system_content.replace("today", today_date)
28
- items = gemini_get_answer(items, system_instu)
29
- print(f"--- AI modification complete ---")
30
- # print(items)
31
- return items
32
  def get_property(it, task_name, url, api_key, model):
33
  properties = {
34
  "Task name": {
@@ -81,23 +65,38 @@ def init_items(items):
81
  # items = ",".join(line.strip() for line in items.splitlines() if line.strip())
82
  return items
83
 
 
 
 
 
84
  @app.route("/", methods=["POST"])
85
  def push():
86
  # items = request.get_json(force=True)
87
- items = request.get_data(as_text=True)
 
 
 
 
 
 
 
88
  items = init_items(items)
89
- items= modified_with_ai(items)
90
  # change to json format
91
  items = json.loads(items)
 
 
 
 
 
 
 
92
  url = "https://api.notion.com/v1/pages"
93
  headers = {
94
  "Authorization": f"Bearer {NOTION_TOKEN}",
95
  "Content-Type": "application/json",
96
  "Notion-Version": "2022-06-28"
97
  }
98
- ai_implemented = "gpt"
99
- if ai_implemented is not None:
100
- ai = load_ai_config(ai_implemented)
101
  # print("ai_config:", ai)
102
  for it in items:
103
  task_name = it.get("Task name", "Untitled Task")
@@ -108,7 +107,8 @@ def push():
108
  else:
109
  print(f"Successfully pushed item: {it.get('Task name', 'Untitled Task')}")
110
  # print(r.status_code, r.text)
111
- return jsonify({"ok": True, "inserted": len(items)})
 
112
 
113
  @app.route("/get", methods=["GET"])
114
  def get_all_data():
@@ -143,4 +143,4 @@ def get_all_data():
143
 
144
  return jsonify({"ok": True, "data": all_pages, "count": len(all_pages)})
145
  if __name__ == "__main__":
146
- app.run(host="0.0.0.0", port=7860)
 
1
+ from flask import Flask, request, jsonify, render_template
2
+ from src.ai_transform import gemini_get_answer, classify_task_with_ai, modified_with_ai
3
+ from src.utils import *
4
  import requests, os, datetime
 
5
  import json
6
+
7
+ # for offline debugging
8
+ if os.path.exists("secret.env"):
9
+ from dotenv import load_dotenv
10
+ load_dotenv("secret.env")
11
  app = Flask(__name__)
12
 
13
  NOTION_TOKEN = os.environ.get("NOTION_TOKEN")
14
  DB_ID = os.environ.get("DB_ID")
 
 
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  def get_property(it, task_name, url, api_key, model):
17
  properties = {
18
  "Task name": {
 
65
  # items = ",".join(line.strip() for line in items.splitlines() if line.strip())
66
  return items
67
 
68
+ @app.route("/", methods=["GET"])
69
+ def index():
70
+ return render_template("index.html")
71
+
72
  @app.route("/", methods=["POST"])
73
  def push():
74
  # items = request.get_json(force=True)
75
+ if request.content_type == 'text/plain':
76
+ items = request.get_data(as_text=True)
77
+ else: # application/json
78
+ items = request.get_json()['text']
79
+ # implement AI
80
+ ai_implemented = "gpt"
81
+ if ai_implemented is not None:
82
+ ai = load_ai_config(ai_implemented)
83
  items = init_items(items)
84
+ items = modified_with_ai(items, url=ai["url"], api_key=ai["api_key"], model=ai["model"])
85
  # change to json format
86
  items = json.loads(items)
87
+ debug_mode = request.args.get("debug") == "true"
88
+ print("Debug mode:", debug_mode)
89
+ if debug_mode:
90
+ return jsonify({"items": items, "debug": True, "ok": True})
91
+
92
+ # push to notion
93
+
94
  url = "https://api.notion.com/v1/pages"
95
  headers = {
96
  "Authorization": f"Bearer {NOTION_TOKEN}",
97
  "Content-Type": "application/json",
98
  "Notion-Version": "2022-06-28"
99
  }
 
 
 
100
  # print("ai_config:", ai)
101
  for it in items:
102
  task_name = it.get("Task name", "Untitled Task")
 
107
  else:
108
  print(f"Successfully pushed item: {it.get('Task name', 'Untitled Task')}")
109
  # print(r.status_code, r.text)
110
+ return jsonify({"ok": True, "inserted": len(items), "debug": False, "items": items})
111
+
112
 
113
  @app.route("/get", methods=["GET"])
114
  def get_all_data():
 
143
 
144
  return jsonify({"ok": True, "data": all_pages, "count": len(all_pages)})
145
  if __name__ == "__main__":
146
+ app.run(host="0.0.0.0", port=7860)# , debug=True
prompt.txt CHANGED
@@ -29,6 +29,7 @@
29
  * **傍晚/夜晚关键词**: "dinner", "supper", "evening", "night", "lecturing", "relaxing", "streaming" 等词语旁的数字,应优先推断为 **PM (12:00 - 23:00)**。
30
  * **早晨/白天关键词**: "breakfast", "morning", "晨会", "standup", "meeting", "work" 等词语旁的数字,应优先推断为 **AM 或工作时间 (07:00 - 18:00)**。
31
  * **无明确线索**: 如果没有任何上下文线索,根据你的世界知识做出最合理的判断(例如,一个在“2点”的“商务会议”极有可能是下午14:00,而不是凌晨02:00)。
 
32
  * 可以推理device,如streaming为Phone,project working一般是电脑(涉及编程)
33
 
34
  ### 5. 示例 (必须严格模仿)
 
29
  * **傍晚/夜晚关键词**: "dinner", "supper", "evening", "night", "lecturing", "relaxing", "streaming" 等词语旁的数字,应优先推断为 **PM (12:00 - 23:00)**。
30
  * **早晨/白天关键词**: "breakfast", "morning", "晨会", "standup", "meeting", "work" 等词语旁的数字,应优先推断为 **AM 或工作时间 (07:00 - 18:00)**。
31
  * **无明确线索**: 如果没有任何上下文线索,根据你的世界知识做出最合理的判断(例如,一个在“2点”的“商务会议”极有可能是下午14:00,而不是凌晨02:00)。
32
+ * 注意:不要返回{current_date}字符,要根据给定的日期替换。
33
  * 可以推理device,如streaming为Phone,project working一般是电脑(涉及编程)
34
 
35
  ### 5. 示例 (必须严格模仿)
ai_transform.py → src/ai_transform.py RENAMED
@@ -3,13 +3,28 @@ import json
3
  import requests
4
  import os
5
  import google.generativeai as genai
6
- from utils import *
7
- # from dotenv import load_dotenv
8
- # load_dotenv("secret.env")
9
  ST_API_PASS = os.environ.get("ST_API_PASSWORD")
10
  ST_api_key = f"Bearer {ST_API_PASS}"
11
  ST_url = "https://spark-api-open.xf-yun.com/v1/chat/completions"
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  def classify_task_with_ai(task_info: str, url=ST_url, api_key=ST_api_key, model=None, categories_filepath: str = "related_tasks.txt", config = None) -> str:
14
  """
15
  使用 ai 模型和 RAG 方式对任务名称进行分类。
@@ -25,7 +40,7 @@ def classify_task_with_ai(task_info: str, url=ST_url, api_key=ST_api_key, model=
25
  api_key : str, optional
26
  API 请求的密钥,默认为 ST_api_key。
27
  model : str, optional
28
- 使用的模型,默认为 None。
29
  返回
30
  ----
31
  str
@@ -79,6 +94,7 @@ def gemini_get_answer(user_input: str, system_prompt: str = "") -> str:
79
  # 2. 创建模型实例
80
  model = genai.GenerativeModel(
81
  model_name="gemini-2.5-flash",
 
82
  system_instruction=system_prompt or None # 空字符串时传 None,避免多余字段
83
  )
84
 
@@ -94,10 +110,10 @@ def _init_model(url):
94
  model = "lite"
95
  # could be extended
96
  # elif "gemini" in url:
97
- # model = "gemini-2.5-flash"
98
  return model
99
 
100
- def _get_answer(message, url=ST_url, api_key=ST_api_key, model=None):
101
  """
102
  Accepts a single string `message` as the prompt and returns the model-generated response.
103
  """
@@ -115,14 +131,19 @@ def _get_answer(message, url=ST_url, api_key=ST_api_key, model=None):
115
  if model is None:
116
  model = _init_model(url)
117
  # 这里的 message 应该是我们之前设计好的、包含示例的完整提示词
 
 
 
 
 
 
 
 
 
 
118
  body = {
119
  "model": model,
120
- "messages": [
121
- {
122
- "role": "user",
123
- "content": message
124
- },
125
- ],
126
  "stream": True
127
  }
128
 
 
3
  import requests
4
  import os
5
  import google.generativeai as genai
6
+ from src.utils import *
 
 
7
  ST_API_PASS = os.environ.get("ST_API_PASSWORD")
8
  ST_api_key = f"Bearer {ST_API_PASS}"
9
  ST_url = "https://spark-api-open.xf-yun.com/v1/chat/completions"
10
 
11
+ with open("prompt.txt", encoding="utf-8") as f:
12
+ system_content = f.read()
13
+
14
+ def modified_with_ai(items, url=ST_url, api_key=ST_api_key, model=None):
15
+ # 在这里调用AI模型对item进行修改
16
+ # today_date = time.strftime("%Y-%m-%d", time.localtime())
17
+ today_date = time_cali()
18
+ system_instu = system_content.replace("today", today_date)
19
+ if "gemini" in url:
20
+ items = gemini_get_answer(items, system_instu)
21
+ else:
22
+ items = _get_answer(items, url, api_key, model, system_instu)
23
+ items = items.replace("{current_date}", today_date)
24
+ print(f"--- AI modification complete ---")
25
+ # print(items)
26
+ return items
27
+
28
  def classify_task_with_ai(task_info: str, url=ST_url, api_key=ST_api_key, model=None, categories_filepath: str = "related_tasks.txt", config = None) -> str:
29
  """
30
  使用 ai 模型和 RAG 方式对任务名称进行分类。
 
40
  api_key : str, optional
41
  API 请求的密钥,默认为 ST_api_key。
42
  model : str, optional
43
+ 使用的模型-> 使用init_model根据url自动选择, 如:gpt-3.5-turbo, 默认为 None。
44
  返回
45
  ----
46
  str
 
94
  # 2. 创建模型实例
95
  model = genai.GenerativeModel(
96
  model_name="gemini-2.5-flash",
97
+ # model_name = "gemini-2.0-flash-lite",
98
  system_instruction=system_prompt or None # 空字符串时传 None,避免多余字段
99
  )
100
 
 
110
  model = "lite"
111
  # could be extended
112
  # elif "gemini" in url:
113
+ # model = "gemini-2.5-lite"
114
  return model
115
 
116
+ def _get_answer(message, url=ST_url, api_key=ST_api_key, model=None, system_instr=None):
117
  """
118
  Accepts a single string `message` as the prompt and returns the model-generated response.
119
  """
 
131
  if model is None:
132
  model = _init_model(url)
133
  # 这里的 message 应该是我们之前设计好的、包含示例的完整提示词
134
+ message_list =[]
135
+ if system_instr is not None:
136
+ message_list.append({
137
+ "role": "system",
138
+ "content": system_instr
139
+ })
140
+ message_list.append({
141
+ "role": "user",
142
+ "content": message
143
+ })
144
  body = {
145
  "model": model,
146
+ "messages": message_list,
 
 
 
 
 
147
  "stream": True
148
  }
149
 
utils.py → src/utils.py RENAMED
@@ -1,4 +1,6 @@
1
  import os
 
 
2
  def load_task_mapping_from_txt(filepath: str = "task_mapping.txt") -> dict:
3
  """
4
  从 txt 文件中读取 "类别名称: ID" 格式的数据,并返回一个字典。
@@ -53,6 +55,16 @@ def load_ai_config(name = "default"):
53
  config["api_key"] = "Bearer " + os.environ.get(f"{API_MODEL}_KEY")
54
  return config
55
 
 
 
 
 
 
 
 
 
 
 
56
  if __name__ == "__main__":
57
  mapping = load_task_mapping_from_txt("related_tasks.txt")
58
  # print(mapping.keys())
 
1
  import os
2
+ import time
3
+
4
  def load_task_mapping_from_txt(filepath: str = "task_mapping.txt") -> dict:
5
  """
6
  从 txt 文件中读取 "类别名称: ID" 格式的数据,并返回一个字典。
 
55
  config["api_key"] = "Bearer " + os.environ.get(f"{API_MODEL}_KEY")
56
  return config
57
 
58
+ def time_cali():
59
+ # 拿到 UTC 时间戳
60
+ ts = time.time()
61
+ # 手动加 8 小时
62
+ beijing_ts = ts + 8*3600
63
+ # 再转回 struct_time
64
+ beijing_t = time.gmtime(beijing_ts)
65
+ # print(time.strftime('%Y-%m-%d', beijing_t))
66
+ return time.strftime('%Y-%m-%d', beijing_t)
67
+
68
  if __name__ == "__main__":
69
  mapping = load_task_mapping_from_txt("related_tasks.txt")
70
  # print(mapping.keys())
templates/index.html ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Notion Task Pusher</title>
6
+ <style>
7
+ /* 黑色主题的样式 */
8
+ body {
9
+ font-family: sans-serif;
10
+ max-width: 80%;
11
+ margin: 40px auto;
12
+ padding: 20px;
13
+ border: 1px solid #444;
14
+ border-radius: 8px;
15
+ background-color: #1a1a1a; /* 深色背景 */
16
+ color: #f0f0f0; /* 浅色文本 */
17
+ }
18
+ h1 {
19
+ color: #00bcd4; /* 强调色,例如青色 */
20
+ }
21
+ p {
22
+ color: #ccc;
23
+ }
24
+ textarea {
25
+ width: 100%;
26
+ height: 150px;
27
+ padding: 10px;
28
+ box-sizing: border-box;
29
+ margin-bottom: 10px;
30
+ border: 1px solid #333;
31
+ background-color: #2c2c2c; /* 输入框背景 */
32
+ color: #f0f0f0; /* 输入框文本颜色 */
33
+ }
34
+ button {
35
+ padding: 10px 15px;
36
+ background-color: #00bcd4; /* 按钮背景使用强调色 */
37
+ color: #1a1a1a; /* 按钮文本为深色 */
38
+ border: none;
39
+ border-radius: 5px;
40
+ cursor: pointer;
41
+ transition: background-color 0.3s;
42
+ }
43
+ button:hover {
44
+ background-color: #008ba3; /* 鼠标悬停时的深一点的强调色 */
45
+ }
46
+ /* #jsonOutput {
47
+ height: 300px;
48
+ margin-top: 20px;
49
+ color: #00bcd4;
50
+ background-color: #111;
51
+ } */
52
+ .container {
53
+ display: flex;
54
+ gap: 20px; /* 左右间距 */
55
+ /* max-width: 1200px; 视情况可删 */
56
+ width: 100%;
57
+ margin: 0 auto; /* 居中 */
58
+ }
59
+
60
+ /* 左侧主体:自适应剩余宽度 */
61
+ .left-main {
62
+ flex: 1;
63
+ }
64
+
65
+ /* 右侧边栏:固定 340px 宽 */
66
+ .right-aside {
67
+ width: 40%;
68
+ }
69
+ .Output {
70
+ width: 100%;
71
+ /* height: 400px; */
72
+ height: 30vh;
73
+ resize: none;
74
+ color: #00bcd4;
75
+ background-color: #111;
76
+ border: 1px solid #00bcd4;
77
+ border-radius: 4px;
78
+ padding: 8px;
79
+ font-family: monospace;
80
+ }
81
+ .Input {
82
+ width: 100%;
83
+ height: 80%; /* 想撑满父级可用 calc(100vh - 40px) */
84
+ resize: none;
85
+ color: #f0f0f0;
86
+ background-color: #2c2c2c;
87
+ border: 1px solid #444;
88
+ border-radius: 4px;
89
+ padding: 8px;
90
+ font-family: monospace;
91
+ }
92
+ #response {
93
+ margin-top: 20px;
94
+ padding: 10px;
95
+ border-left: 5px solid #00bcd4; /* 响应区使用强调色 */
96
+ background-color: #2c2c2c;
97
+ white-space: pre-wrap;
98
+ color: #f0f0f0;
99
+ }
100
+ </style>
101
+ </head>
102
+ <body>
103
+ <h1>Notion 任务快速录入</h1>
104
+ <div class="container">
105
+ <!-- 左侧主体内容 -->
106
+ <main class="left-main">
107
+ <textarea class="Input" id="taskInput" placeholder="例如:明天上午10点 设计评审会, 下午4-6点 团队建设 high priority, 用pad"></textarea>
108
+ <button onclick="submitTasks()">提交到 Notion</button>
109
+ <label style="margin-left: 20px; color: #ccc;">
110
+ <input type="checkbox" id="debugMode" style="vertical-align: middle;"> 调试模式
111
+ </label>
112
+ <div id="response"></div>
113
+ </main>
114
+
115
+ <!-- 右侧边栏 -->
116
+ <aside class="right-aside">
117
+ <textarea class="Output" id="jsonOutput" placeholder="AI 转换结果将在此处显示..." readonly></textarea>
118
+ </aside>
119
+ </div>
120
+ <script>
121
+ async function submitTasks() {
122
+ const input = document.getElementById('taskInput').value;
123
+ const debugMode = document.getElementById('debugMode').checked;
124
+ const responseDiv = document.getElementById('response');
125
+ const jsonOutputArea = document.getElementById('jsonOutput');
126
+ responseDiv.innerHTML = '正在处理...';
127
+ responseDiv.style.display = 'block';
128
+ jsonOutputArea.value = ''; // 清空之前的内容
129
+ // 构建 URL, 如果是调试模式,则添加 ?debug=true
130
+ const url = debugMode ? '/?debug=true' : '/';
131
+
132
+ try {
133
+ const response = await fetch(url, {
134
+ method: 'POST',
135
+ headers: {
136
+ 'Content-Type': 'text/plain'
137
+ },
138
+ body: input
139
+ });
140
+
141
+ if (!response.ok) {
142
+ const text = await response.text();
143
+ // 抛出错误,将被 catch 块捕获
144
+ throw new Error(`HTTP Error ${response.status}: ${text}`);
145
+ }
146
+
147
+ const data = await response.json();
148
+
149
+ if (data.ok) {
150
+ if (data.debug) {
151
+ // 调试模式的特殊显示
152
+ responseDiv.innerHTML = `✅ **调试模式开启:未推送到 Notion**<br><br>AI 转换后的 JSON 数据`;
153
+ responseDiv.style.borderLeftColor = 'orange';
154
+ } else {
155
+ // 正常模式的显示
156
+ responseDiv.innerHTML = `✅ 提交成功! 共插入 ${data.inserted} 个任务。`;
157
+ responseDiv.style.borderLeftColor = '#00bcd4';
158
+ }
159
+ // 在右侧文本区域显示 AI 转换结果
160
+ jsonOutputArea.value = JSON.stringify(data.items, null, 2);
161
+ } else {
162
+ // 业务逻辑错误
163
+ responseDiv.innerHTML = `❌ 提交失败: ${data.error || '未知错误'}`;
164
+ responseDiv.style.borderLeftColor = 'red';
165
+ }
166
+
167
+ } catch (error) {
168
+ // 网络或代码/JSON 解析错误
169
+ responseDiv.innerHTML = `❌ 发生错误: ${error.message}`;
170
+ responseDiv.style.borderLeftColor = 'red';
171
+ console.error('Error:', error);
172
+ }
173
+ }
174
+ function _submitTasks() {
175
+ const input = document.getElementById('taskInput').value;
176
+ const debugMode = document.getElementById('debugMode').checked; // 获取调试模式状态
177
+ const responseDiv = document.getElementById('response');
178
+ responseDiv.innerHTML = '正在处理...';
179
+
180
+ // 构建 URL, 如果是调试模式,则添加 ?debug=true
181
+ const url = debugMode ? '/?debug=true' : '/';
182
+
183
+ fetch(url, {
184
+ method: 'POST',
185
+ headers: {
186
+ 'Content-Type': 'text/plain'
187
+ },
188
+ body: input
189
+ })
190
+ .then(response => {
191
+ if (!response.ok) {
192
+ return response.text().then(text => Promise.reject(new Error(`HTTP Error ${response.status}: ${text}`)));
193
+ }
194
+ // 无论是否调试模式,都期望返回 JSON
195
+ return response.json();
196
+ })
197
+ .then(data => {
198
+ if (data.ok) {
199
+ if (data.debug) {
200
+ // 调试模式的特殊显示
201
+ responseDiv.innerHTML = `✅ **调试模式开启:未推送到 Notion**<br><br>AI 转换后的 JSON 数据:\n${JSON.stringify(data.ai_output, null, 2)}`;
202
+ responseDiv.style.borderLeftColor = 'orange';
203
+ } else {
204
+ responseDiv.innerHTML = `✅ 提交成功! 共插入 ${data.inserted} 个任务。`;
205
+ responseDiv.style.borderLeftColor = '#00bcd4';
206
+ }
207
+ } else {
208
+ responseDiv.innerHTML = `❌ 提交失败: ${data.error || '未知错误'}`;
209
+ responseDiv.style.borderLeftColor = 'red';
210
+ }
211
+ })
212
+ .catch(error => {
213
+ responseDiv.innerHTML = `❌ 发生错误: ${error.message}`;
214
+ responseDiv.style.borderLeftColor = 'red';
215
+ console.error('Error:', error);
216
+ });
217
+ }
218
+ </script>
219
+ </body>
220
+ </html>