-
Notifications
You must be signed in to change notification settings - Fork 62
feat: agent secret scope #285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mlikasam-askui
wants to merge
2
commits into
main
Choose a base branch
from
feat/secrets-scope
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """Example demonstrating the secret scope. | ||
|
|
||
| Secrets let the agent *use* sensitive values (e.g. type a password) while the value is | ||
| never sent to the LLM. The model only ever sees the placeholder | ||
| ``<|secret|>NAME<|secret|>``; the real value is substituted into tool calls at execution | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the |
||
| time. Literal values are also redacted from the LLM history, tool outputs and the cache. | ||
|
|
||
| Two ways to provide a secret are shown: | ||
| 1. Hardcoded value (handy for a quick demo only). | ||
| 2. Read from an environment variable (recommended for real usage). | ||
|
|
||
| Required environment variables (see .env): | ||
| - ASKUI_WORKSPACE_ID, ASKUI_TOKEN - for the default AskUI providers | ||
| - APP_PASSWORD - the example login password, read at runtime (see below) | ||
|
|
||
| Set the secret in your shell before running (do NOT hardcode real secrets in code): | ||
| export APP_PASSWORD="my-real-password" | ||
|
|
||
| Note: a secret typed into a *visible* field can still appear in screenshots sent to the | ||
| model; on-screen secrets cannot currently be hidden. | ||
| """ | ||
|
|
||
| import logging | ||
| import os | ||
|
|
||
| from askui import ComputerAgent, Secret | ||
|
|
||
| logging.basicConfig( | ||
| level=logging.INFO, | ||
| format="[%(levelname)s] %(asctime)s %(pathname)s:%(lineno)d | %(message)s", | ||
| ) | ||
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def secrets_from_env() -> list[Secret]: | ||
| """Build secrets by reading their values from environment variables. | ||
|
|
||
| This is the recommended approach: keep real values out of source code and pass them | ||
| in from the environment. We only *read* env vars here (never set them in code). | ||
| """ | ||
| return [ | ||
| Secret( | ||
| name="password", | ||
| value=os.environ["APP_PASSWORD"], | ||
| description="the application login password", | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| def secrets_hardcoded() -> list[Secret]: | ||
| """Build secrets with hardcoded values. | ||
|
|
||
| Convenient for a quick local demo, but never commit real secrets to source control. | ||
| """ | ||
| return [ | ||
| Secret( | ||
| name="password", | ||
| value="hunter2-demo-only", | ||
| description="the application login password", | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| def run_with_agent_level_secrets() -> None: | ||
| """Define secrets on the agent so they apply to every act()/type() call.""" | ||
| with ComputerAgent(secrets=secrets_from_env()) as agent: | ||
| # The agent emits the placeholder; the real value is typed at execution time. | ||
| agent.act("Log in as 'admin' using the password") | ||
|
|
||
| # Deterministic typing also resolves the placeholder at the OS boundary. | ||
| agent.click("Password field") | ||
| agent.type("<|secret|>password<|secret|>") | ||
|
|
||
|
|
||
| def run_with_per_call_secrets() -> None: | ||
| """Provide secrets only for a single act() call (overrides agent-level on name).""" | ||
| with ComputerAgent() as agent: | ||
| agent.act( | ||
| "Enter the one-time PIN into the verification field", | ||
| secrets=[ | ||
| Secret( | ||
| name="pin", | ||
| value=os.environ.get("APP_OTP", "000000"), | ||
| description="6-digit one-time PIN", | ||
| ), | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| def run_with_hardcoded_secret() -> None: | ||
| """Quick demo using a hardcoded secret value (not for production).""" | ||
| with ComputerAgent(secrets=secrets_hardcoded()) as agent: | ||
| agent.act("Log in using the password") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| # Pick the variant you want to try: | ||
| run_with_agent_level_secrets() | ||
| # run_with_per_call_secrets() | ||
| # run_with_hardcoded_secret() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| from askui.container import telemetry | ||
| from askui.locators.locators import Locator | ||
| from askui.models.models import Point | ||
| from askui.models.shared.secrets import Secret | ||
| from askui.models.shared.settings import ActSettings, LocateSettings, MessageSettings | ||
| from askui.models.shared.tools import Tool | ||
| from askui.models.shared.truncation_strategies import TruncationStrategy | ||
|
|
@@ -59,6 +60,13 @@ class ComputerAgent(Agent): | |
| act_tools (list[Tool] | None, optional): Additional tools to make available for | ||
| the `act()` method for every call. Same tools can instead be passed per call | ||
| via `act(..., tools=[...])` (see example below). | ||
| secrets (list[Secret] | None, optional): Sensitive values (e.g. passwords) the | ||
| agent may use but the LLM must never see. The model only sees the placeholder | ||
| `<|secret|>NAME<|secret|>`; the real value is substituted at execution time and is | ||
| kept out of the LLM prompt, reporter, logs and cache. Also usable in | ||
| deterministic `type()` and overridable per call via `act(..., secrets=[...])`. | ||
| Note: a secret typed into a visible field may still appear in screenshots sent | ||
| to the model; on-screen secrets cannot currently be hidden. | ||
|
|
||
| Example: | ||
| ```python | ||
|
|
@@ -99,6 +107,7 @@ class ComputerAgent(Agent): | |
| "act_tools", | ||
| "callbacks", | ||
| "truncation_strategy", | ||
| "secrets", | ||
| } | ||
| ) | ||
| @validate_call(config=ConfigDict(arbitrary_types_allowed=True)) | ||
|
|
@@ -112,6 +121,7 @@ def __init__( | |
| act_tools: list[Tool] | None = None, | ||
| callbacks: list[ConversationCallback] | None = None, | ||
| truncation_strategy: TruncationStrategy | None = None, | ||
| secrets: list[Secret] | None = None, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you not instroduced a SecretVault? I want to have the possibilitiy to plugin different stores. |
||
| ) -> None: | ||
| reporter = CompositeReporter(reporters=reporters) | ||
| self.tools = tools or AgentToolbox( | ||
|
|
@@ -128,6 +138,7 @@ def __init__( | |
| settings=settings, | ||
| callbacks=callbacks, | ||
| truncation_strategy=truncation_strategy, | ||
| secrets=secrets, | ||
| ) | ||
| self.act_agent_os_facade: ComputerAgentOsFacade = ComputerAgentOsFacade( | ||
| self.tools.os | ||
|
|
@@ -320,7 +331,9 @@ def type( | |
| agent.type("text", locator="Input field", offset=(5, 0)) # Click 5 pixels right of "Input field", then type | ||
| ``` | ||
| """ | ||
| msg = f'type "{text}"' | ||
| # Reporter/logs see the placeholder; the OS receives the resolved value. | ||
| redacted_text = self._redact_secrets(text) | ||
| msg = f'type "{redacted_text}"' | ||
| if locator is not None: | ||
| msg += f" into {locator}" | ||
| if clear: | ||
|
|
@@ -337,7 +350,7 @@ def type( | |
| ) | ||
| logger.debug("Agent received instruction to %s", msg) | ||
| self._reporter.add_message("User", msg) | ||
| self.tools.os.type(text) | ||
| self.tools.os.type(self._resolve_secrets(text)) | ||
|
|
||
| @telemetry.record_call() | ||
| @validate_call | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this not moved to a docs section?