fix(inclusive-language): replace blacklist terminology with denylist

- Replace blacklisted_tools configuration key and comments in config/artemis.jsonc and README.md with denylisted_tools
- Update AgentGlobalConfig and AgentConfig with denylisted_tools and validation aliases
- Update AgentConfigBuilder and Explorer agent to use denylist conventions
- Update Admin Console replay manager and data engine trace filters
- Update all associated test suites and mock configurations
- Add license headers and clean up test imports
This commit is contained in:
yaoyao-open-source committed 2026-08-13 14:03:49 -07:00
1 parent 5d2283deaa
commit 701a74b546
24 files changed
+201 -67

No files matched your search

+4 -4
View File
@@ -1,7 +1,7 @@
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
https://www.apache.org/licenses/ http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -187,16 +187,16 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright 2026 Google LLC. Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
+1 -1
View File
@@ -303,7 +303,7 @@ All model configurations, thinking budgets, agent capabilities, and tool filteri
// 4. Agent Capabilities & Tool Filtering // 4. Agent Capabilities & Tool Filtering
"agent": { "agent": {
"explorer_versions": { "operator": "flash", "validator": "flash" }, "explorer_versions": { "operator": "flash", "validator": "flash" },
"blacklisted_tools": { "denylisted_tools": {
"explorer": ["ask_image_processor", "get_ocr_list", "inspect_region"] "explorer": ["ask_image_processor", "get_ocr_list", "inspect_region"]
} }
} }
+15
View File
@@ -1 +1,16 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Artemis / Latte Applications Package.""" """Artemis / Latte Applications Package."""
+14
View File
@@ -1,3 +1,17 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Artemis Admin & Trace Console Package.""" """Artemis Admin & Trace Console Package."""
from apps.admin_console.server import app from apps.admin_console.server import app
+12
View File
@@ -1 +1,13 @@
# Copyright 2026 Google LLC # Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+12
View File
@@ -1 +1,13 @@
# Copyright 2026 Google LLC # Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
@@ -1 +1,13 @@
# Copyright 2026 Google LLC # Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+10 -10
View File
@@ -52,7 +52,7 @@ REPLAY_TOOLS_CONFIG = {
"module": "artemis.tools.explorer_tool", "module": "artemis.tools.explorer_tool",
"function": "_run_explorer_logic", "function": "_run_explorer_logic",
"agent_name": "explorer", "agent_name": "explorer",
"blacklist_args": ["ctx", "state"], "denylist_args": ["ctx", "state"],
"fallback_mappings": {}, "fallback_mappings": {},
"llm_span_name": "gemini_explorer_call", "llm_span_name": "gemini_explorer_call",
}, },
@@ -65,7 +65,7 @@ REPLAY_TOOLS_CONFIG = {
"module": "artemis.tools.image_processor_tool", "module": "artemis.tools.image_processor_tool",
"function": "_run_image_processor_logic", "function": "_run_image_processor_logic",
"agent_name": "image_processor", "agent_name": "image_processor",
"blacklist_args": ["ctx", "state"], "denylist_args": ["ctx", "state"],
"fallback_mappings": {}, "fallback_mappings": {},
"llm_span_name": "gemini_image_processor_call", "llm_span_name": "gemini_image_processor_call",
}, },
@@ -1996,14 +1996,14 @@ class ReplayManager:
user_submits = user_submits or {} user_submits = user_submits or {}
sig = inspect.signature(tool_fn) sig = inspect.signature(tool_fn)
tool_args = {} tool_args = {}
blacklist = tool_cfg.get("blacklist_args", []) denylist = tool_cfg.get("denylist_args", [])
fallback_mappings = tool_cfg.get("fallback_mappings", {}) fallback_mappings = tool_cfg.get("fallback_mappings", {})
user_submits = user_submits or {} user_submits = user_submits or {}
sig = inspect.signature(tool_fn) sig = inspect.signature(tool_fn)
tool_args = {} tool_args = {}
for name, param in sig.parameters.items(): for name, param in sig.parameters.items():
if name in blacklist: if name in denylist:
if name == "ctx": if name == "ctx":
tool_args["ctx"] = ctx tool_args["ctx"] = ctx
elif name == "state": elif name == "state":
@@ -2012,7 +2012,7 @@ class ReplayManager:
if name in user_submits: if name in user_submits:
val = user_submits[name] val = user_submits[name]
if param.annotation == bool and isinstance(val, str): if param.annotation is bool and isinstance(val, str):
val = val.lower() == "true" val = val.lower() == "true"
tool_args[name] = val tool_args[name] = val
else: else:
@@ -2086,10 +2086,10 @@ class ReplayManager:
tool_fn = getattr(mod, tool_cfg["function"]) tool_fn = getattr(mod, tool_cfg["function"])
sig = inspect.signature(tool_fn) sig = inspect.signature(tool_fn)
blacklist = tool_cfg.get("blacklist_args", []) denylist = tool_cfg.get("denylist_args", [])
explorer_params = [] explorer_params = []
for name, param in sig.parameters.items(): for name, param in sig.parameters.items():
if name in blacklist: if name in denylist:
continue continue
default_val = "" default_val = ""
if param.default is not inspect.Parameter.empty: if param.default is not inspect.Parameter.empty:
@@ -2106,7 +2106,7 @@ class ReplayManager:
) )
): ):
input_type = "textarea" input_type = "textarea"
elif param.annotation == bool: elif param.annotation is bool:
input_type = "checkbox" input_type = "checkbox"
elif param.annotation in (int, float): elif param.annotation in (int, float):
input_type = "number" input_type = "number"
@@ -2181,12 +2181,12 @@ class ReplayManager:
all_tool_defaults = {} all_tool_defaults = {}
for t_name, sig in tool_signatures.items(): for t_name, sig in tool_signatures.items():
t_cfg = REPLAY_TOOLS_CONFIG[t_name] t_cfg = REPLAY_TOOLS_CONFIG[t_name]
blacklist = t_cfg.get("blacklist_args", []) denylist = t_cfg.get("denylist_args", [])
fallback_mappings = t_cfg.get("fallback_mappings", {}) fallback_mappings = t_cfg.get("fallback_mappings", {})
defaults = {} defaults = {}
for name, param in sig.parameters.items(): for name, param in sig.parameters.items():
if name in blacklist: if name in denylist:
continue continue
val = "" val = ""
+12
View File
@@ -1 +1,13 @@
# Copyright 2026 Google LLC # Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+12
View File
@@ -1 +1,13 @@
# Copyright 2026 Google LLC # Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+12
View File
@@ -1 +1,13 @@
# Copyright 2026 Google LLC # Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+29 -29
View File
@@ -223,8 +223,8 @@ UNIVERSAL_EXPLORER_TOOLS = [
class Explorer: class Explorer:
BLACKLIST_TEMPLATE = ( DENYLIST_TEMPLATE = (
"\n# TOOL BLACKLIST\n- The following tools are blacklisted and cannot be used: {tools}\n" "\n# TOOL DENYLIST\n- The following tools are denylisted and cannot be used: {tools}\n"
) )
TOOLS = [ TOOLS = [
types.FunctionDeclaration( types.FunctionDeclaration(
@@ -445,12 +445,12 @@ class Explorer:
self.next_img_id = 1 self.next_img_id = 1
try: try:
agent_cfg = getattr(self.ctx, "agent_config", None) agent_cfg = getattr(self.ctx, "agent_config", None)
blacklisted_config = ( denylisted_config = (
getattr(agent_cfg, "blacklisted_tools", {}).get("explorer", []) if agent_cfg else [] getattr(agent_cfg, "denylisted_tools", {}).get("explorer", []) if agent_cfg else []
) )
self.blacklisted_tools = set(blacklisted_config) self.denylisted_tools = set(denylisted_config)
except (TypeError, AttributeError): except (TypeError, AttributeError):
self.blacklisted_tools = set() self.denylisted_tools = set()
self.http_client = None self.http_client = None
self.turn_latencies = [] self.turn_latencies = []
self.turn_cached_tokens = [] self.turn_cached_tokens = []
@@ -514,8 +514,8 @@ class Explorer:
] ]
def get_exposed_tools(self, only_submit: bool = False) -> list[types.FunctionDeclaration]: def get_exposed_tools(self, only_submit: bool = False) -> list[types.FunctionDeclaration]:
"""Returns the list of tools, filtering out blacklisted ones.""" """Returns the list of tools, filtering out denylisted ones."""
tools = [tool for tool in self.TOOLS if tool.name not in self.blacklisted_tools] tools = [tool for tool in self.TOOLS if tool.name not in self.denylisted_tools]
if only_submit: if only_submit:
tools = [tool for tool in tools if tool.name == "submit_answer"] tools = [tool for tool in tools if tool.name == "submit_answer"]
return tools return tools
@@ -1143,11 +1143,11 @@ class Explorer:
"""Executes Explorer reasoning loop via Universal LangChain ChatModel.""" """Executes Explorer reasoning loop via Universal LangChain ChatModel."""
llm = get_llm(self.ctx, name="explorer") llm = get_llm(self.ctx, name="explorer")
# Filter universal tools based on blacklist # Filter universal tools based on denylist
exposed_tools = [ exposed_tools = [
t t
for t in UNIVERSAL_EXPLORER_TOOLS for t in UNIVERSAL_EXPLORER_TOOLS
if t["function"]["name"] not in self.blacklisted_tools if t["function"]["name"] not in self.denylisted_tools
] ]
bound_llm = llm.bind_tools(exposed_tools) bound_llm = llm.bind_tools(exposed_tools)
@@ -1251,12 +1251,12 @@ class Explorer:
args = tc.get("args", {}) args = tc.get("args", {})
call_id = tc.get("id", f"call_{iterations}_{name}") call_id = tc.get("id", f"call_{iterations}_{name}")
if name in self.blacklisted_tools: if name in self.denylisted_tools:
messages.append( messages.append(
ToolMessage( ToolMessage(
tool_call_id=call_id, tool_call_id=call_id,
name=name, name=name,
content=f"Tool '{name}' is blacklisted and unavailable.", content=f"Tool '{name}' is denylisted and unavailable.",
) )
) )
continue continue
@@ -1428,12 +1428,12 @@ class Explorer:
# Resolve parameters # Resolve parameters
mode = version.capitalize() mode = version.capitalize()
version_blacklist = set() version_denylist = set()
if version == "ultra": if version == "ultra":
max_iterations = 8 max_iterations = 8
elif version == "pro": elif version == "pro":
max_iterations = 3 max_iterations = 3
version_blacklist = { version_denylist = {
"ask_image_processor", "ask_image_processor",
"get_ocr_list", "get_ocr_list",
"inspect_region", "inspect_region",
@@ -1441,21 +1441,21 @@ class Explorer:
} }
else: else:
max_iterations = 3 max_iterations = 3
version_blacklist = { version_denylist = {
"ask_image_processor", "ask_image_processor",
"get_ocr_list", "get_ocr_list",
"inspect_region", "inspect_region",
} }
try: try:
blacklisted_config = ( denylisted_config = (
self.ctx.agent_config.blacklisted_tools.get("explorer", []) self.ctx.agent_config.denylisted_tools.get("explorer", [])
if self.ctx.agent_config if self.ctx.agent_config
else [] else []
) )
self.blacklisted_tools = version_blacklist.union(set(blacklisted_config)) self.denylisted_tools = version_denylist.union(set(denylisted_config))
except (TypeError, AttributeError): except (TypeError, AttributeError):
self.blacklisted_tools = version_blacklist self.denylisted_tools = version_denylist
# 2. Prepare Native Tools declarations # 2. Prepare Native Tools declarations
self.width = 1080 self.width = 1080
@@ -1544,14 +1544,14 @@ class Explorer:
prompt_parts.append("") prompt_parts.append("")
prompt_template = "\n".join(prompt_parts) prompt_template = "\n".join(prompt_parts)
prompt_template += "\n{blakclist}" prompt_template += "\n{denylist}"
if self.blacklisted_tools: if self.denylisted_tools:
blacklist_section = self.BLACKLIST_TEMPLATE.format( denylist_section = self.DENYLIST_TEMPLATE.format(
tools=", ".join(sorted(self.blacklisted_tools)) tools=", ".join(sorted(self.denylisted_tools))
) )
else: else:
blacklist_section = "" denylist_section = ""
prompt_template = prompt_template.replace("{blakclist}", blacklist_section) prompt_template = prompt_template.replace("{denylist}", denylist_section)
explore_info = EXPLORE_DESCRIPTIONS.get(version, EXPLORE_DESCRIPTIONS["pro"]) explore_info = EXPLORE_DESCRIPTIONS.get(version, EXPLORE_DESCRIPTIONS["pro"])
version_prompt = explore_info.get("version_prompt") version_prompt = explore_info.get("version_prompt")
@@ -2039,20 +2039,20 @@ Initial marked UI elements list (corresponding to numbers ①, ②... in the ima
"name": name, "name": name,
"args": args, "args": args,
} }
if name in self.blacklisted_tools: if name in self.denylisted_tools:
logger.warning( logger.warning(
"Explorer attempted to call blacklisted tool" "Explorer attempted to call denylisted tool"
f" '{name}'. Blocking execution." f" '{name}'. Blocking execution."
) )
tool_call_trace["response"] = { tool_call_trace["response"] = {
"error": (f"Tool '{name}' is blacklisted and unavailable.") "error": (f"Tool '{name}' is denylisted and unavailable.")
} }
turn_record["tool_calls"].append(tool_call_trace) turn_record["tool_calls"].append(tool_call_trace)
tool_response_parts.append( tool_response_parts.append(
types.Part.from_function_response( types.Part.from_function_response(
name=fc.name, name=fc.name,
response={ response={
"error": (f"Tool '{name}' is blacklisted and unavailable.") "error": (f"Tool '{name}' is denylisted and unavailable.")
}, },
) )
) )
+4 -3
View File
@@ -18,7 +18,7 @@ import os
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from pydantic import BaseModel, Field, model_validator from pydantic import AliasChoices, BaseModel, Field, model_validator
from artemis.config.constants import ( from artemis.config.constants import (
AGENT_CONFIG_FILENAME, AGENT_CONFIG_FILENAME,
@@ -286,9 +286,10 @@ class AgentGlobalConfig(BaseModel):
default_factory=OutputterConfig, default_factory=OutputterConfig,
description="Outputter synthesis agent runtime options.", description="Outputter synthesis agent runtime options.",
) )
blacklisted_tools: dict[str, list[str]] = Field( denylisted_tools: dict[str, list[str]] = Field(
default_factory=dict, default_factory=dict,
description="List of blacklisted tools per agent.", description="List of denylisted tools per agent.",
validation_alias=AliasChoices("denylisted_tools", "blocklisted_tools"),
) )
video_analyzer: VideoAnalyzerConfig = Field( video_analyzer: VideoAnalyzerConfig = Field(
default_factory=VideoAnalyzerConfig, default_factory=VideoAnalyzerConfig,
+2 -2
View File
@@ -851,7 +851,7 @@ class DataEngine:
interleaved_events = [] interleaved_events = []
try: try:
blacklist = ( denylist = (
"operator", "operator",
"perception", "perception",
"planner", "planner",
@@ -891,7 +891,7 @@ class DataEngine:
if is_relevant_llm: if is_relevant_llm:
relevant_traces.append(t) relevant_traces.append(t)
elif t.type in ("tool", "agent") and t.name not in blacklist: elif t.type in ("tool", "agent") and t.name not in denylist:
relevant_traces.append(t) relevant_traces.append(t)
# Sort by timestamp to ensure exact chronological order # Sort by timestamp to ensure exact chronological order
+5 -5
View File
@@ -73,7 +73,7 @@ class AgentConfigBuilder:
agent_cfg = load_agent_config() agent_cfg = load_agent_config()
self._explorer = agent_cfg.explorer self._explorer = agent_cfg.explorer
self._explorer_versions = agent_cfg.explorer_versions self._explorer_versions = agent_cfg.explorer_versions
self._blacklisted_tools = agent_cfg.blacklisted_tools self._denylisted_tools = agent_cfg.denylisted_tools
self._enable_video_ledger = agent_cfg.video_analyzer.enable_ledger self._enable_video_ledger = agent_cfg.video_analyzer.enable_ledger
if agent_cfg.video_analyzer.enabled is not None: if agent_cfg.video_analyzer.enabled is not None:
self._video_recording_tools_enabled = agent_cfg.video_analyzer.enabled self._video_recording_tools_enabled = agent_cfg.video_analyzer.enabled
@@ -431,9 +431,9 @@ class AgentConfigBuilder:
self._explorer_versions = versions self._explorer_versions = versions
return self return self
def with_blacklisted_tools(self, tools: dict[str, list[str]]) -> "AgentConfigBuilder": def with_denylisted_tools(self, tools: dict[str, list[str]]) -> "AgentConfigBuilder":
"""Configure blacklisted tools.""" """Configure denylisted tools."""
self._blacklisted_tools = tools self._denylisted_tools = tools
return self return self
def build(self, validate_profiles: bool = True) -> AgentConfig: def build(self, validate_profiles: bool = True) -> AgentConfig:
@@ -505,7 +505,7 @@ class AgentConfigBuilder:
pro=self._pro, pro=self._pro,
explorer=self._explorer, explorer=self._explorer,
explorer_versions=self._explorer_versions, explorer_versions=self._explorer_versions,
blacklisted_tools=self._blacklisted_tools, denylisted_tools=self._denylisted_tools,
enable_video_ledger=self._enable_video_ledger, enable_video_ledger=self._enable_video_ledger,
) )
+5 -2
View File
@@ -25,7 +25,7 @@ from artemis.config import (
from artemis.context import DevicePlatform from artemis.context import DevicePlatform
from artemis.sdk.types.task import AgentProfile, TaskRequestCommon from artemis.sdk.types.task import AgentProfile, TaskRequestCommon
from artemis.utils.video import detect_video_tools_enabled from artemis.utils.video import detect_video_tools_enabled
from pydantic import BaseModel, Field from pydantic import AliasChoices, BaseModel, Field
class _CyFunctionDetectorMeta(type): class _CyFunctionDetectorMeta(type):
@@ -125,7 +125,10 @@ class AgentConfig(BaseModel):
explorer: ExplorerConfig = Field(default_factory=ExplorerConfig) explorer: ExplorerConfig = Field(default_factory=ExplorerConfig)
explorer_versions: dict[str, Literal["flash", "pro", "ultra"]] explorer_versions: dict[str, Literal["flash", "pro", "ultra"]]
blacklisted_tools: dict[str, list[str]] denylisted_tools: dict[str, list[str]] = Field(
default_factory=dict,
validation_alias=AliasChoices("denylisted_tools", "blocklisted_tools"),
)
enable_video_ledger: bool = True enable_video_ledger: bool = True
model_config = {"arbitrary_types_allowed": True} model_config = {"arbitrary_types_allowed": True}
+2 -2
View File
@@ -204,8 +204,8 @@
"operator": "flash", "operator": "flash",
"validator": "flash" "validator": "flash"
}, },
// 🚫 Per-agent Tool Blacklists // 🚫 Per-agent Tool Denylists
"blacklisted_tools": { "denylisted_tools": {
"explorer": [ "explorer": [
"ask_image_processor", "ask_image_processor",
"get_ocr_list", "get_ocr_list",
@@ -29,7 +29,7 @@ def create_mock_context() -> MagicMock:
ctx.device.device_height = 2400 ctx.device.device_height = 2400
ctx.llm_config = MagicMock() ctx.llm_config = MagicMock()
ctx.agent_config = MagicMock() ctx.agent_config = MagicMock()
ctx.agent_config.blacklisted_tools = {"explorer": []} ctx.agent_config.denylisted_tools = {"explorer": []}
ctx.data_engine = None ctx.data_engine = None
ctx.adb_client = None ctx.adb_client = None
return ctx return ctx
+14
View File
@@ -1,3 +1,17 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import time import time
import uiautomator2 as u2 import uiautomator2 as u2
+1 -1
View File
@@ -77,7 +77,7 @@ def artemis_context(inputs_dir, mock_adb_client):
ctx.trace_dir = trace_out ctx.trace_dir = trace_out
ctx.agent_config = MagicMock() ctx.agent_config = MagicMock()
ctx.agent_config.blacklisted_tools = {} ctx.agent_config.denylisted_tools = {}
return ctx return ctx
+5 -5
View File
@@ -843,7 +843,7 @@ async def test_explorer_initial_visual_marking_previous_screenshot_on_the_fly_oc
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_explorer_blacklisted_tool(): async def test_explorer_denylisted_tool():
# Mock context and state # Mock context and state
mock_ctx = MagicMock(spec=ArtemisContext) mock_ctx = MagicMock(spec=ArtemisContext)
mock_ctx.device = MagicMock() mock_ctx.device = MagicMock()
@@ -855,12 +855,12 @@ async def test_explorer_blacklisted_tool():
mock_ctx.llm_config.explorer = MagicMock() mock_ctx.llm_config.explorer = MagicMock()
mock_ctx.llm_config.explorer.model = "gemini-3.6-flash" mock_ctx.llm_config.explorer.model = "gemini-3.6-flash"
mock_ctx.agent_config = MagicMock() mock_ctx.agent_config = MagicMock()
mock_ctx.agent_config.blacklisted_tools = {"explorer": ["search_xml_ocr"]} mock_ctx.agent_config.denylisted_tools = {"explorer": ["search_xml_ocr"]}
mock_state = MagicMock(spec=State) mock_state = MagicMock(spec=State)
mock_state.latest_screenshot = "/tmp/test_screenshot.jpg" mock_state.latest_screenshot = "/tmp/test_screenshot.jpg"
# Response 1: Model calls search_xml_ocr (which is blacklisted) # Response 1: Model calls search_xml_ocr (which is denylisted)
mock_response1 = MagicMock() mock_response1 = MagicMock()
mock_response1.text = "" mock_response1.text = ""
mock_func_call1 = MagicMock() mock_func_call1 = MagicMock()
@@ -958,7 +958,7 @@ async def test_explorer_blacklisted_tool():
assert mock_client.aio.models.generate_content.call_count == 2 assert mock_client.aio.models.generate_content.call_count == 2
# Verify the contents passed to the second call contain the error response # Verify the contents passed to the second call contain the error response
# indicating that the tool is blacklisted and unavailable. # indicating that the tool is denylisted and unavailable.
call_args = mock_client.aio.models.generate_content.call_args_list[1] call_args = mock_client.aio.models.generate_content.call_args_list[1]
contents = call_args[1]["contents"] contents = call_args[1]["contents"]
@@ -978,7 +978,7 @@ async def test_explorer_blacklisted_tool():
for part in tool_content.parts: for part in tool_content.parts:
func_resp = part.function_response func_resp = part.function_response
if func_resp and func_resp.name == "search_xml_ocr": if func_resp and func_resp.name == "search_xml_ocr":
assert "blacklisted and unavailable" in func_resp.response["error"] assert "denylisted and unavailable" in func_resp.response["error"]
found_error = True found_error = True
assert found_error assert found_error
+1 -1
View File
@@ -43,7 +43,7 @@ def mock_context(tmp_path):
ctx.device.device_height = 2400 ctx.device.device_height = 2400
ctx.agent_config = Mock() ctx.agent_config = Mock()
ctx.agent_config.blacklisted_tools = {} ctx.agent_config.denylisted_tools = {}
ctx.data_engine = Mock() ctx.data_engine = Mock()
ctx.data_engine.base_dir = tmp_path ctx.data_engine.base_dir = tmp_path
+1 -1
View File
@@ -136,7 +136,7 @@ def test_agent_config_loading():
assert agent_cfg.explorer.flash_mode == "flash" assert agent_cfg.explorer.flash_mode == "flash"
assert agent_cfg.explorer.pro_mode == "flash" assert agent_cfg.explorer.pro_mode == "flash"
assert agent_cfg.explorer.caching is True assert agent_cfg.explorer.caching is True
assert "explorer" in agent_cfg.blacklisted_tools assert "explorer" in agent_cfg.denylisted_tools
assert agent_cfg.video_analyzer.enable_ledger is True assert agent_cfg.video_analyzer.enable_ledger is True
assert agent_cfg.planner_validation.enabled is False assert agent_cfg.planner_validation.enabled is False
assert agent_cfg.planner_validation.similarity_threshold == 0.85 assert agent_cfg.planner_validation.similarity_threshold == 0.85
+15
View File
@@ -1,3 +1,18 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio import asyncio
import pytest import pytest
from artemis.context import ArtemisContext, DeviceContext, DevicePlatform from artemis.context import ArtemisContext, DeviceContext, DevicePlatform