fix: stabilize agent file change previews
This commit is contained in:
@@ -16,6 +16,7 @@ if "SOVEREIGNAI_DATA_DIR" not in os.environ:
|
||||
from app.main import (
|
||||
AgentRequest,
|
||||
_execute_agent,
|
||||
_explicit_file_change_requested,
|
||||
_explicit_workspace_search_queries,
|
||||
_knowledge_context_for_model,
|
||||
_is_knowledge_answer_insufficient,
|
||||
@@ -235,6 +236,130 @@ class AgentSkillTests(unittest.TestCase):
|
||||
self.assertEqual(result["skill"], "code_review")
|
||||
self.assertNotIn("proposal", result)
|
||||
|
||||
def test_explicit_file_change_retries_with_forced_preview_tool(self) -> None:
|
||||
completions = [
|
||||
{"choices": [{"message": {"content": "سأفحص الطلب."}}]},
|
||||
{
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "proposal-retry",
|
||||
"function": {
|
||||
"name": "propose_file_change",
|
||||
"arguments": json.dumps(
|
||||
{
|
||||
"path": "README.md",
|
||||
"operation": "update",
|
||||
"content": "# Updated\n",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{"choices": [{"message": {"content": "تم إعداد المعاينة فقط."}}]},
|
||||
]
|
||||
proposal = {
|
||||
"token": "preview-token",
|
||||
"path": "README.md",
|
||||
"operation": "update",
|
||||
"diff": "--- a/README.md\n+++ b/README.md\n+updated\n",
|
||||
"expires_in_seconds": 600,
|
||||
}
|
||||
with (
|
||||
patch(
|
||||
"app.main.workspace.create_change_preview",
|
||||
return_value=proposal,
|
||||
) as create_preview,
|
||||
patch(
|
||||
"app.main.get_completion",
|
||||
new=AsyncMock(side_effect=completions),
|
||||
) as model,
|
||||
):
|
||||
result = asyncio.run(
|
||||
_execute_agent(
|
||||
AgentRequest(
|
||||
task="نفّذ تعديلًا صريحًا: حدّث README.md وأعد معاينة diff فقط.",
|
||||
workspace_path=self.workspace,
|
||||
workspace_files=["README.md"],
|
||||
skill_id="code_review",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(model.await_count, 3)
|
||||
forced_payload = model.await_args_list[1].args[0]
|
||||
self.assertEqual(
|
||||
forced_payload["tool_choice"],
|
||||
{"type": "function", "function": {"name": "propose_file_change"}},
|
||||
)
|
||||
self.assertEqual(
|
||||
[tool["function"]["name"] for tool in forced_payload["tools"]],
|
||||
["propose_file_change"],
|
||||
)
|
||||
self.assertEqual(len(forced_payload["messages"]), 2)
|
||||
self.assertIn("أعد نداء أداة propose_file_change الآن", forced_payload["messages"][0]["content"])
|
||||
create_preview.assert_called_once()
|
||||
self.assertEqual(result["proposal"]["diff"], proposal["diff"])
|
||||
self.assertEqual(result["tool"], "propose_file_change")
|
||||
|
||||
def test_explicit_file_change_fails_clearly_if_retry_has_no_tool_call(self) -> None:
|
||||
completions = [
|
||||
{"choices": [{"message": {"content": "سأعمل على التعديل."}}]},
|
||||
{"choices": [{"message": {"content": "تعذر إنشاء معاينة."}}]},
|
||||
]
|
||||
with (
|
||||
patch(
|
||||
"app.main.get_completion",
|
||||
new=AsyncMock(side_effect=completions),
|
||||
) as model,
|
||||
patch("app.main.workspace.create_change_preview") as create_preview,
|
||||
):
|
||||
with self.assertRaises(HTTPException) as error:
|
||||
asyncio.run(
|
||||
_execute_agent(
|
||||
AgentRequest(
|
||||
task="نفّذ تعديلًا صريحًا: حدّث README.md وأعد معاينة diff فقط.",
|
||||
workspace_path=self.workspace,
|
||||
workspace_files=["README.md"],
|
||||
skill_id="code_review",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(error.exception.status_code, 502)
|
||||
self.assertIn("لم يُكتب أي ملف", error.exception.detail)
|
||||
self.assertEqual(model.await_count, 2)
|
||||
create_preview.assert_not_called()
|
||||
|
||||
def test_file_change_retry_requires_positive_intent_and_file_target(self) -> None:
|
||||
self.assertTrue(
|
||||
_explicit_file_change_requested(
|
||||
"نفّذ تعديلًا صريحًا على README.md", selected_files=False
|
||||
)
|
||||
)
|
||||
self.assertTrue(
|
||||
_explicit_file_change_requested("أضف شرحًا", selected_files=True)
|
||||
)
|
||||
self.assertFalse(
|
||||
_explicit_file_change_requested(
|
||||
"راجع الملفات دون تعديل", selected_files=True
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
_explicit_file_change_requested(
|
||||
"كيف أعدّل README.md؟", selected_files=False
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
_explicit_file_change_requested("أنشئ خطة اختبار", selected_files=False)
|
||||
)
|
||||
|
||||
def test_model_cannot_call_a_tool_after_file_proposal_ends_tool_access(self) -> None:
|
||||
completions = [
|
||||
{"choices": [{"message": {"tool_calls": [{
|
||||
|
||||
@@ -2,7 +2,10 @@ from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
|
||||
from scripts.generate_flutter_license_triage import candidate_license
|
||||
from scripts.generate_flutter_license_triage import (
|
||||
candidate_license,
|
||||
license_signatures,
|
||||
)
|
||||
|
||||
|
||||
class FlutterLicenseTriageTests(unittest.TestCase):
|
||||
@@ -16,9 +19,26 @@ class FlutterLicenseTriageTests(unittest.TestCase):
|
||||
self.assertEqual(candidate_license(bsd3)[0], "BSD-3-Clause")
|
||||
|
||||
def test_keeps_ambiguous_notice_unclassified(self) -> None:
|
||||
self.assertIsNone(candidate_license("Copyright 2026 Example Authors. All rights reserved." )[0])
|
||||
self.assertIsNone(
|
||||
candidate_license("Copyright 2026 Example Authors. All rights reserved.")[0]
|
||||
)
|
||||
mixed = "Mozilla Public License Version 2.0. Apache License Version 2.0."
|
||||
self.assertIsNone(candidate_license(mixed)[0])
|
||||
self.assertEqual(
|
||||
sorted(license_signatures(mixed)),
|
||||
["Apache-2.0", "MPL-2.0"],
|
||||
)
|
||||
|
||||
def test_detects_license_signatures_in_composite_notice(self) -> None:
|
||||
text = """Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met. Neither the name of the project
|
||||
may be used to endorse products. THIS SOFTWARE IS PROVIDED AS IS; DISCLAIMER.
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
"""
|
||||
signatures = license_signatures(text)
|
||||
self.assertEqual(sorted(signatures), ["Apache-2.0", "BSD-3-Clause"])
|
||||
self.assertIsNone(candidate_license(text)[0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user