feat: add Chinese UI, Responses models, and mirrored Windows setup
CI / Python 3.12 / ubuntu-latest (push) Canceled after 0s
CI / Python 3.12 / windows-latest (push) Canceled after 0s
CI / Frontend tests and production build (push) Canceled after 0s
CI / Build and smoke-test Python packages (push) Canceled after 0s

Localize the web console and persist custom model and capability settings.
Use OpenAI Responses exclusively and move runtime limits into environment configuration.
Bootstrap uv and the virtual environment on Windows, support optional China mirrors,
and package source distributions without environments, build output, or credentials.

Update regression tests and repository quality checks for the new behavior.
This commit is contained in:
chenmingxuan committed 2026-09-22 19:16:17 +08:00
1 parent 371aa6df56
commit 2ddec53954
183 files changed
+3427 -11857

No files matched your search

@@ -55,9 +55,9 @@ def test_tool_declaration_mirrors_the_args_schema(tool):
assert lc_tool.args_schema is tool.args_schema
# And the GenAI export agrees on names and required fields.
genai = tool.to_genai_declaration()
assert set(genai.parameters.properties) == set(properties)
assert list(genai.parameters.required or []) == declaration.parameters["required"]
genai = tool.to_tool_declaration()
assert set(genai.parameters["properties"]) == set(properties)
assert list(genai.parameters.get("required") or []) == declaration.parameters["required"]
def test_declared_types_and_enums():
@@ -47,10 +47,10 @@ def test_append_note_pure_tool_subclass():
assert append_note_pure.args_schema == AppendNoteArgs
# GenAI FunctionDeclaration export
declaration = append_note_pure.to_genai_declaration()
declaration = append_note_pure.to_tool_declaration()
assert declaration.name == "append_note_pure"
assert "key" in declaration.parameters.properties
assert "content" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "content" in declaration.parameters["properties"]
@pytest.mark.asyncio
+3 -3
View File
@@ -51,10 +51,10 @@ def test_append_note_tool_subclass():
assert append_note.args_schema == AppendNoteArgs
# GenAI FunctionDeclaration export
declaration = append_note.to_genai_declaration()
declaration = append_note.to_tool_declaration()
assert declaration.name == "append_note"
assert "key" in declaration.parameters.properties
assert "content" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "content" in declaration.parameters["properties"]
# Wrapper check
assert append_note_wrapper is not None
+9 -9
View File
@@ -436,9 +436,9 @@ def test_run_adb_command_tool_subclass():
assert run_adb_command.args_schema == RunAdbCommandArgs
# GenAI FunctionDeclaration export
declaration = run_adb_command.to_genai_declaration()
declaration = run_adb_command.to_tool_declaration()
assert declaration.name == "run_adb_command"
assert "CommandLine" in declaration.parameters.properties
assert "CommandLine" in declaration.parameters["properties"]
@pytest.mark.asyncio
@@ -479,9 +479,9 @@ def test_manage_task_tool_subclass():
assert manage_task.args_schema == ManageTaskArgs
# GenAI FunctionDeclaration export
declaration = manage_task.to_genai_declaration()
declaration = manage_task.to_tool_declaration()
assert declaration.name == "manage_task"
assert "Action" in declaration.parameters.properties
assert "Action" in declaration.parameters["properties"]
@pytest.mark.asyncio
@@ -541,10 +541,10 @@ def test_analyze_task_output_tool_subclass():
assert analyze_task_output.args_schema == AnalyzeTaskOutputArgs
# GenAI FunctionDeclaration export
declaration = analyze_task_output.to_genai_declaration()
declaration = analyze_task_output.to_tool_declaration()
assert declaration.name == "analyze_task_output"
assert "TaskId" in declaration.parameters.properties
assert "Query" in declaration.parameters.properties
assert "TaskId" in declaration.parameters["properties"]
assert "Query" in declaration.parameters["properties"]
@pytest.mark.asyncio
@@ -606,9 +606,9 @@ def test_run_short_adb_command_tool_subclass():
assert run_short_adb_command.args_schema == RunShortAdbCommandArgs
# GenAI FunctionDeclaration export
declaration = run_short_adb_command.to_genai_declaration()
declaration = run_short_adb_command.to_tool_declaration()
assert declaration.name == "run_short_adb_command"
assert "CommandLine" in declaration.parameters.properties
assert "CommandLine" in declaration.parameters["properties"]
@pytest.mark.asyncio
+2 -2
View File
@@ -195,9 +195,9 @@ class TestCommitteeTool(unittest.IsolatedAsyncioTestCase):
self.assertEqual(ask_committee.args_schema, AskCommitteeArgs)
# GenAI FunctionDeclaration export
declaration = ask_committee.to_genai_declaration()
declaration = ask_committee.to_tool_declaration()
self.assertEqual(declaration.name, "ask_committee")
self.assertIn("avatar_directive", declaration.parameters.properties)
self.assertIn("avatar_directive", declaration.parameters["properties"])
# Wrapper check
self.assertIsNotNone(ask_committee_wrapper)
+3 -3
View File
@@ -109,10 +109,10 @@ def test_tool_contract_is_tier_agnostic():
assert fields["context_feedback"].default == ""
assert fields["context_feedback"].description == ASK_EXPLORER_CONTEXT_FEEDBACK_DESCRIPTION
declaration = ask_explorer.to_genai_declaration()
declaration = ask_explorer.to_tool_declaration()
assert declaration.name == "ask_explorer"
assert set(declaration.parameters.properties) == {"query", "context_feedback"}
assert declaration.parameters.required == ["query"]
assert set(declaration.parameters["properties"]) == {"query", "context_feedback"}
assert declaration.parameters.get("required") == ["query"]
@pytest.mark.parametrize("version", [None, "flash", "pro", "ultra"])
@@ -88,7 +88,7 @@ def test_ask_image_processor_tool_subclass():
assert ask_image_processor.args_schema == AskVisionCoderArgs
# GenAI FunctionDeclaration export
declaration = ask_image_processor.to_genai_declaration()
declaration = ask_image_processor.to_tool_declaration()
assert declaration.name == "ask_image_processor"
assert "instruction" in declaration.parameters.properties
assert "target_image_id" in declaration.parameters.properties
assert "instruction" in declaration.parameters["properties"]
assert "target_image_id" in declaration.parameters["properties"]
+2 -2
View File
@@ -61,9 +61,9 @@ def test_launch_app_tool_subclass():
assert launch_app.args_schema == LaunchAppArgs
# GenAI FunctionDeclaration export
declaration = launch_app.to_genai_declaration()
declaration = launch_app.to_tool_declaration()
assert declaration.name == "launch_app"
assert "app_name" in declaration.parameters.properties
assert "app_name" in declaration.parameters["properties"]
# Wrapper check
assert launch_app_wrapper is not None
@@ -47,7 +47,7 @@ def test_list_notes_pure_tool_subclass():
assert list_notes_pure.args_schema == ListNotesArgs
# GenAI FunctionDeclaration export
declaration = list_notes_pure.to_genai_declaration()
declaration = list_notes_pure.to_tool_declaration()
assert declaration.name == "list_notes_pure"
+1 -1
View File
@@ -51,7 +51,7 @@ def test_list_notes_tool_subclass():
assert list_notes.args_schema == ListNotesArgs
# GenAI FunctionDeclaration export
declaration = list_notes.to_genai_declaration()
declaration = list_notes.to_tool_declaration()
assert declaration.name == "list_notes"
# Wrapper check
+2 -2
View File
@@ -101,9 +101,9 @@ def test_analyze_logs_tool_subclass():
assert analyze_logs.args_schema == AnalyzeLogsArgs
# GenAI FunctionDeclaration export
declaration = analyze_logs.to_genai_declaration()
declaration = analyze_logs.to_tool_declaration()
assert declaration.name == "analyze_logs"
assert "specific_query" in declaration.parameters.properties
assert "specific_query" in declaration.parameters["properties"]
# Wrapper check
assert analyze_logs_wrapper is not None
@@ -66,14 +66,14 @@ def test_object_detection_tool_subclass():
assert operator_object_detection.args_schema == OperatorObjectDetectionArgs
# GenAI FunctionDeclaration export
declaration = object_detection.to_genai_declaration()
declaration = object_detection.to_tool_declaration()
assert declaration.name == "object_detection"
assert "image_path" in declaration.parameters.properties
assert "queries" in declaration.parameters.properties
assert "image_path" in declaration.parameters["properties"]
assert "queries" in declaration.parameters["properties"]
op_declaration = operator_object_detection.to_genai_declaration()
op_declaration = operator_object_detection.to_tool_declaration()
assert op_declaration.name == "object_detection"
assert "queries" in op_declaration.parameters.properties
assert "queries" in op_declaration.parameters["properties"]
# Wrappers
assert object_detection_wrapper.tool_fn_getter == get_object_detector_tool
+1 -1
View File
@@ -77,7 +77,7 @@ def test_ocr_tool_subclass():
assert ocr_recognition.args_schema == OcrArgs
# GenAI FunctionDeclaration export
declaration = ocr_recognition.to_genai_declaration()
declaration = ocr_recognition.to_tool_declaration()
assert declaration.name == "ocr_recognition"
# Wrapper check
+1 -1
View File
@@ -59,7 +59,7 @@ def test_get_ui_hierarchy_tool_subclass():
assert get_ui_hierarchy.args_schema == GetUiHierarchyArgs
# GenAI FunctionDeclaration export
declaration = get_ui_hierarchy.to_genai_declaration()
declaration = get_ui_hierarchy.to_tool_declaration()
assert declaration.name == "get_ui_hierarchy"
# Wrapper check
+4 -4
View File
@@ -46,11 +46,11 @@ def test_read_logs_tool_subclass():
assert read_logs.args_schema == ReadLogsArgs
# GenAI FunctionDeclaration export
declaration = read_logs.to_genai_declaration()
declaration = read_logs.to_tool_declaration()
assert declaration.name == "read_logs"
assert "lines" in declaration.parameters.properties
assert "since_time" in declaration.parameters.properties
assert "until_time" in declaration.parameters.properties
assert "lines" in declaration.parameters["properties"]
assert "since_time" in declaration.parameters["properties"]
assert "until_time" in declaration.parameters["properties"]
@pytest.mark.asyncio
+4 -4
View File
@@ -47,11 +47,11 @@ def test_read_note_pure_tool_subclass():
assert read_note_pure.args_schema == ReadNoteArgs
# GenAI FunctionDeclaration export
declaration = read_note_pure.to_genai_declaration()
declaration = read_note_pure.to_tool_declaration()
assert declaration.name == "read_note_pure"
assert "key" in declaration.parameters.properties
assert "start_line" in declaration.parameters.properties
assert "end_line" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "start_line" in declaration.parameters["properties"]
assert "end_line" in declaration.parameters["properties"]
@pytest.mark.asyncio
+4 -4
View File
@@ -51,11 +51,11 @@ def test_read_note_tool_subclass():
assert read_note.args_schema == ReadNoteArgs
# GenAI FunctionDeclaration export
declaration = read_note.to_genai_declaration()
declaration = read_note.to_tool_declaration()
assert declaration.name == "read_note"
assert "key" in declaration.parameters.properties
assert "start_line" in declaration.parameters.properties
assert "end_line" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "start_line" in declaration.parameters["properties"]
assert "end_line" in declaration.parameters["properties"]
# Wrapper check
assert read_note_wrapper is not None
+3 -3
View File
@@ -47,10 +47,10 @@ def test_save_note_pure_tool_subclass():
assert save_note_pure.args_schema == SaveNoteArgs
# GenAI FunctionDeclaration export
declaration = save_note_pure.to_genai_declaration()
declaration = save_note_pure.to_tool_declaration()
assert declaration.name == "save_note_pure"
assert "key" in declaration.parameters.properties
assert "content" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "content" in declaration.parameters["properties"]
@pytest.mark.asyncio
+3 -3
View File
@@ -51,10 +51,10 @@ def test_save_note_tool_subclass():
assert save_note.args_schema == SaveNoteArgs
# GenAI FunctionDeclaration export
declaration = save_note.to_genai_declaration()
declaration = save_note.to_tool_declaration()
assert declaration.name == "save_note"
assert "key" in declaration.parameters.properties
assert "content" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "content" in declaration.parameters["properties"]
# Wrapper check
assert save_note_wrapper is not None
+7 -7
View File
@@ -186,14 +186,14 @@ def test_search_logs_tool_subclass():
assert search_logs.args_schema == SearchLogsArgs
# GenAI FunctionDeclaration export
declaration = search_logs.to_genai_declaration()
declaration = search_logs.to_tool_declaration()
assert declaration.name == "search_logs"
assert "keyword" in declaration.parameters.properties
assert "is_regex" in declaration.parameters.properties
assert "lines" in declaration.parameters.properties
assert "since_time" in declaration.parameters.properties
assert "until_time" in declaration.parameters.properties
assert "context_lines" in declaration.parameters.properties
assert "keyword" in declaration.parameters["properties"]
assert "is_regex" in declaration.parameters["properties"]
assert "lines" in declaration.parameters["properties"]
assert "since_time" in declaration.parameters["properties"]
assert "until_time" in declaration.parameters["properties"]
assert "context_lines" in declaration.parameters["properties"]
@pytest.mark.asyncio
@@ -48,11 +48,11 @@ def test_update_note_pure_tool_subclass():
assert update_note_pure.args_schema == UpdateNoteArgs
# GenAI FunctionDeclaration export
declaration = update_note_pure.to_genai_declaration()
declaration = update_note_pure.to_tool_declaration()
assert declaration.name == "update_note_pure"
assert "key" in declaration.parameters.properties
assert "target" in declaration.parameters.properties
assert "replacement" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "target" in declaration.parameters["properties"]
assert "replacement" in declaration.parameters["properties"]
@pytest.mark.asyncio
+4 -4
View File
@@ -52,11 +52,11 @@ def test_update_note_tool_subclass():
assert update_note.args_schema == UpdateNoteArgs
# GenAI FunctionDeclaration export
declaration = update_note.to_genai_declaration()
declaration = update_note.to_tool_declaration()
assert declaration.name == "update_note"
assert "key" in declaration.parameters.properties
assert "target" in declaration.parameters.properties
assert "replacement" in declaration.parameters.properties
assert "key" in declaration.parameters["properties"]
assert "target" in declaration.parameters["properties"]
assert "replacement" in declaration.parameters["properties"]
# Wrapper check
assert update_note_wrapper is not None
@@ -44,10 +44,10 @@ def test_video_analyzer_pure_tool_subclass():
assert video_analyzer_pure.args_schema == VideoAnalyzerArgs
# GenAI FunctionDeclaration export
declaration = video_analyzer_pure.to_genai_declaration()
declaration = video_analyzer_pure.to_tool_declaration()
assert declaration.name == "video_analyzer_pure"
assert "time_description" in declaration.parameters.properties
assert "purpose" in declaration.parameters.properties
assert "time_description" in declaration.parameters["properties"]
assert "purpose" in declaration.parameters["properties"]
@pytest.mark.asyncio
+3 -3
View File
@@ -52,10 +52,10 @@ def test_video_analyzer_tool_subclass():
assert video_analyzer.args_schema == VideoAnalyzerArgs
# GenAI FunctionDeclaration export
declaration = video_analyzer.to_genai_declaration()
declaration = video_analyzer.to_tool_declaration()
assert declaration.name == "video_analyzer"
assert "time_description" in declaration.parameters.properties
assert "purpose" in declaration.parameters.properties
assert "time_description" in declaration.parameters["properties"]
assert "purpose" in declaration.parameters["properties"]
@pytest.mark.asyncio
+2 -2
View File
@@ -47,9 +47,9 @@ def test_wait_tool_subclass():
assert wait.args_schema == WaitArgs
# GenAI FunctionDeclaration export
declaration = wait.to_genai_declaration()
declaration = wait.to_tool_declaration()
assert declaration.name == "wait"
assert "seconds" in declaration.parameters.properties
assert "seconds" in declaration.parameters["properties"]
# Wrapper check
assert wait_wrapper is not None