mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-10-05 02:41:50 +00:00
Merge 6761eb5bf3 into d1e367d0ed
This commit is contained in:
commit
32ce3f3485
3 changed files with 165 additions and 0 deletions
20
openspace/skills/default-utf8-encoding/SKILL.md
Normal file
20
openspace/skills/default-utf8-encoding/SKILL.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
name: default-utf8-encoding
|
||||||
|
description: Baseline file encoding policy. Always use UTF-8 for created or edited text files unless the project explicitly requires another encoding.
|
||||||
|
---
|
||||||
|
|
||||||
|
# UTF-8 Baseline
|
||||||
|
|
||||||
|
This is a mandatory baseline skill for local development.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
1. Default encoding for all newly created or edited text files is UTF-8.
|
||||||
|
2. Preserve existing non-UTF-8 encoding only when the target file is already using it and changing encoding may break runtime behavior.
|
||||||
|
3. When uncertain about file encoding, prefer safe read/modify/write steps that keep file content valid and avoid mojibake.
|
||||||
|
4. For JSON, Markdown, YAML, Python, TypeScript, and shell scripts, treat UTF-8 as the standard unless the repository explicitly states otherwise.
|
||||||
|
|
||||||
|
## Output Hygiene
|
||||||
|
|
||||||
|
1. Avoid introducing garbled characters caused by encoding mismatch.
|
||||||
|
2. Keep line endings and formatting consistent with repository conventions.
|
||||||
|
|
@ -19,6 +19,8 @@ from openspace.utils.logging import Logger
|
||||||
|
|
||||||
logger = Logger.get_logger(__name__)
|
logger = Logger.get_logger(__name__)
|
||||||
|
|
||||||
|
MANDATORY_SKILL_NAME = "default-utf8-encoding"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class OpenSpaceConfig:
|
class OpenSpaceConfig:
|
||||||
|
|
@ -731,6 +733,20 @@ class OpenSpace:
|
||||||
"selected": [],
|
"selected": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Always inject built-in UTF-8 baseline guidance when available.
|
||||||
|
forced_skill_ids: List[str] = []
|
||||||
|
forced_meta = self._skill_registry.get_skill_by_name(MANDATORY_SKILL_NAME)
|
||||||
|
if forced_meta:
|
||||||
|
already_selected = {s.skill_id for s in selected}
|
||||||
|
if forced_meta.skill_id not in already_selected:
|
||||||
|
selected.insert(0, forced_meta)
|
||||||
|
forced_skill_ids.append(forced_meta.skill_id)
|
||||||
|
logger.debug(f"Forced baseline skill: {forced_meta.skill_id}")
|
||||||
|
|
||||||
|
if selection_record is not None and forced_skill_ids:
|
||||||
|
selection_record["forced_skills"] = forced_skill_ids
|
||||||
|
selection_record["selected"] = [s.skill_id for s in selected]
|
||||||
|
|
||||||
# Record skill selection to metadata.json
|
# Record skill selection to metadata.json
|
||||||
if self._recording_manager and selection_record:
|
if self._recording_manager and selection_record:
|
||||||
# Add model info to the record
|
# Add model info to the record
|
||||||
|
|
|
||||||
129
tests/test_utf8_baseline_skill.py
Normal file
129
tests/test_utf8_baseline_skill.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import importlib
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
def _install_tool_layer_stubs():
|
||||||
|
agents_mod = types.ModuleType("openspace.agents")
|
||||||
|
agents_mod.GroundingAgent = type("GroundingAgent", (), {})
|
||||||
|
|
||||||
|
llm_mod = types.ModuleType("openspace.llm")
|
||||||
|
llm_mod.LLMClient = type("LLMClient", (), {"__init__": lambda self, *a, **k: None})
|
||||||
|
|
||||||
|
grounding_client_mod = types.ModuleType("openspace.grounding.core.grounding_client")
|
||||||
|
grounding_client_mod.GroundingClient = type("GroundingClient", (), {})
|
||||||
|
|
||||||
|
config_mod = types.ModuleType("openspace.config")
|
||||||
|
config_mod.get_config = lambda: None
|
||||||
|
config_mod.load_config = lambda *args, **kwargs: None
|
||||||
|
|
||||||
|
config_loader_mod = types.ModuleType("openspace.config.loader")
|
||||||
|
config_loader_mod.get_agent_config = lambda *args, **kwargs: None
|
||||||
|
|
||||||
|
recording_mod = types.ModuleType("openspace.recording")
|
||||||
|
recording_mod.RecordingManager = type("RecordingManager", (), {})
|
||||||
|
|
||||||
|
skill_engine_mod = types.ModuleType("openspace.skill_engine")
|
||||||
|
skill_engine_mod.SkillRegistry = type("SkillRegistry", (), {})
|
||||||
|
skill_engine_mod.ExecutionAnalyzer = type("ExecutionAnalyzer", (), {})
|
||||||
|
skill_engine_mod.SkillStore = type("SkillStore", (), {})
|
||||||
|
|
||||||
|
evolver_mod = types.ModuleType("openspace.skill_engine.evolver")
|
||||||
|
evolver_mod.SkillEvolver = type("SkillEvolver", (), {})
|
||||||
|
|
||||||
|
logging_mod = types.ModuleType("openspace.utils.logging")
|
||||||
|
|
||||||
|
class Logger:
|
||||||
|
@staticmethod
|
||||||
|
def get_logger(name):
|
||||||
|
return types.SimpleNamespace(
|
||||||
|
info=lambda *a, **k: None,
|
||||||
|
debug=lambda *a, **k: None,
|
||||||
|
warning=lambda *a, **k: None,
|
||||||
|
error=lambda *a, **k: None,
|
||||||
|
)
|
||||||
|
|
||||||
|
logging_mod.Logger = Logger
|
||||||
|
|
||||||
|
stubs = {
|
||||||
|
"openspace.agents": agents_mod,
|
||||||
|
"openspace.llm": llm_mod,
|
||||||
|
"openspace.grounding.core.grounding_client": grounding_client_mod,
|
||||||
|
"openspace.config": config_mod,
|
||||||
|
"openspace.config.loader": config_loader_mod,
|
||||||
|
"openspace.recording": recording_mod,
|
||||||
|
"openspace.skill_engine": skill_engine_mod,
|
||||||
|
"openspace.skill_engine.evolver": evolver_mod,
|
||||||
|
"openspace.utils.logging": logging_mod,
|
||||||
|
}
|
||||||
|
|
||||||
|
originals = {name: sys.modules.get(name) for name in stubs}
|
||||||
|
sys.modules.update(stubs)
|
||||||
|
return originals
|
||||||
|
|
||||||
|
|
||||||
|
class UTF8BaselineSkillTests(unittest.IsolatedAsyncioTestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls._originals = _install_tool_layer_stubs()
|
||||||
|
sys.modules.pop("openspace.tool_layer", None)
|
||||||
|
cls.tool_layer = importlib.import_module("openspace.tool_layer")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
sys.modules.pop("openspace.tool_layer", None)
|
||||||
|
for name, module in cls._originals.items():
|
||||||
|
if module is None:
|
||||||
|
sys.modules.pop(name, None)
|
||||||
|
else:
|
||||||
|
sys.modules[name] = module
|
||||||
|
|
||||||
|
async def test_select_and_inject_skills_always_includes_utf8_baseline(self):
|
||||||
|
config = self.tool_layer.OpenSpaceConfig()
|
||||||
|
openspace = self.tool_layer.OpenSpace(config)
|
||||||
|
|
||||||
|
forced_meta = types.SimpleNamespace(
|
||||||
|
skill_id="default-utf8-encoding__imp_abc12345",
|
||||||
|
name="default-utf8-encoding",
|
||||||
|
description="baseline",
|
||||||
|
)
|
||||||
|
|
||||||
|
class FakeRegistry:
|
||||||
|
async def select_skills_with_llm(self, task, llm_client, max_skills, skill_quality):
|
||||||
|
return [], {"method": "llm", "selected": []}
|
||||||
|
|
||||||
|
def get_skill_by_name(self, name):
|
||||||
|
if name == "default-utf8-encoding":
|
||||||
|
return forced_meta
|
||||||
|
return None
|
||||||
|
|
||||||
|
def build_context_injection(self, skills, backends=None):
|
||||||
|
return "|".join(s.skill_id for s in skills)
|
||||||
|
|
||||||
|
injected = {}
|
||||||
|
|
||||||
|
class FakeAgent:
|
||||||
|
backend_scope = ["shell"]
|
||||||
|
|
||||||
|
def set_skill_context(self, context_text, skill_ids):
|
||||||
|
injected["context_text"] = context_text
|
||||||
|
injected["skill_ids"] = list(skill_ids)
|
||||||
|
|
||||||
|
def clear_skill_context(self):
|
||||||
|
injected["cleared"] = True
|
||||||
|
|
||||||
|
openspace._skill_registry = FakeRegistry()
|
||||||
|
openspace._grounding_agent = FakeAgent()
|
||||||
|
openspace._recording_manager = None
|
||||||
|
openspace._skill_store = None
|
||||||
|
openspace._get_skill_selection_llm = lambda: object()
|
||||||
|
|
||||||
|
selected = await openspace._select_and_inject_skills("any task")
|
||||||
|
|
||||||
|
self.assertTrue(selected)
|
||||||
|
self.assertIn(forced_meta.skill_id, injected["skill_ids"])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
Add table
Reference in a new issue