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:
1 parent
5d2283deaa
commit
701a74b546
24 files changed
+201
-67
No files matched your search
@@ -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.
|
||||||
@@ -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"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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."""
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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 = ""
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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.")
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in new issue
Block a user