Skip to content

Commit 7c83b2d

Browse files
authored
Merge branch 'main' into more-detailed-list-apps
2 parents 28ff984 + f7f6837 commit 7c83b2d

92 files changed

Lines changed: 5152 additions & 653 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,72 @@ Quick reference to important project files:
478478
- **Architecture Details:** `contributing/adk_project_overview_and_architecture.md`
479479
- **Contributing Guide:** `CONTRIBUTING.md`
480480
- **LLM Context:** `llms.txt` (summarized), `llms-full.txt` (comprehensive)
481+
482+
## Python Tips
483+
484+
### General Python Best Practices
485+
486+
* **Constants:** Use immutable global constant collections (tuple, frozenset, immutabledict) to avoid hard-to-find bugs. Prefer constants over wild string/int literals, especially for dictionary keys, pathnames, and enums.
487+
* **Naming:** Name mappings like `value_by_key` to enhance readability in lookups (e.g., `item = item_by_id[id]`).
488+
* **Readability:** Use f-strings for concise string formatting, but use lazy-evaluated `%`-based templates for logging. Use `repr()` or `pprint.pformat()` for human-readable debug messages. Use `_` as a separator in numeric literals to improve readability.
489+
* **Comprehensions:** Use list, set, and dict comprehensions for building collections concisely.
490+
* **Iteration:** Iterate directly over containers without indices. Use `enumerate()` when you need the index, `dict.items()` for keys and values, and `zip()` for parallel iteration.
491+
* **Built-ins:** Leverage built-in functions like `all()`, `any()`, `reversed()`, `sum()`, etc., to write more concise and efficient code.
492+
* **Flattening Lists:** Use `itertools.chain.from_iterable()` to flatten a list of lists efficiently without unnecessary copying.
493+
* **String Methods:** Use `startswith()` and `endswith()` with a tuple of strings to check for multiple prefixes or suffixes at once.
494+
* **Decorators:** Use decorators to add common functionality (like logging, timing, caching) to functions without modifying their core logic. Use `functools.wraps()` to preserve the original function's metadata.
495+
* **Context Managers:** Use `with` statements and context managers (from `contextlib` or custom classes with `__enter__`/`__exit__`) to ensure resources are properly initialized and torn down, even in the presence of exceptions.
496+
* **Else Clauses:** Utilize the `else` clause in `try/except` blocks (runs if no exception), and in `for/while` loops (runs if the loop completes without a `break`) to write more expressive and less error-prone code.
497+
* **Single Assignment:** Prefer single-assignment form (assign to a variable once) over assign-and-mutate to reduce bugs and improve readability. Use conditional expressions where appropriate.
498+
* **Equality vs. Identity:** Use `is` or `is not` for singleton comparisons (e.g., `None`, `True`, `False`). Use `==` for value comparison.
499+
* **Object Comparisons:** When implementing custom classes, be careful with `__eq__`. Return `NotImplemented` for unhandled types. Consider edge cases like subclasses and hashing. Prefer using `attrs` or `dataclasses` to handle this automatically.
500+
* **Hashing:** If objects are equal, their hashes must be equal. Ensure attributes used in `__hash__` are immutable. Disable hashing with `__hash__ = None` if custom `__eq__` is implemented without a proper `__hash__`.
501+
* **`__init__()` vs. `__new__()`:** `__new__()` creates the object, `__init__()` initializes it. For immutable types, modifications must happen in `__new__()`.
502+
* **Default Arguments:** NEVER use mutable default arguments. Use `None` as a sentinel value instead.
503+
* **`__add__()` vs. `__iadd__()`:** `x += y` (in-place add) can modify the object in-place if `__iadd__` is implemented (like for lists), while `x = x + y` creates a new object. This matters when multiple variables reference the same object.
504+
* **Properties:** Use `@property` to create getters and setters only when needed, maintaining a simple attribute access syntax. Avoid properties for computationally expensive operations or those that can fail.
505+
* **Modules for Namespacing:** Use modules as the primary mechanism for grouping and namespacing code elements, not classes. Avoid `@staticmethod` and methods that don't use `self`.
506+
* **Argument Passing:** Python is call-by-value, where the values are object references (pointers). Assignment binds a name to an object. Modifying a mutable object through one name affects all names bound to it.
507+
* **Keyword/Positional Arguments:** Use `*` to force keyword-only arguments and `/` to force positional-only arguments. This can prevent argument transposition errors and make APIs clearer, especially for functions with multiple arguments of the same type.
508+
* **Type Hinting:** Annotate code with types to improve readability, debuggability, and maintainability. Use abstract types from `collections.abc` for container annotations (e.g., `Sequence`, `Mapping`, `Iterable`). Annotate return values, including `None`. Choose the most appropriate abstract type for function arguments and return types.
509+
* **`NewType`:** Use `typing.NewType` to create distinct types from primitives (like `int` or `str`) to prevent argument transposition and improve type safety.
510+
* **`__repr__()` vs. `__str__()`:** Implement `__repr__()` for unambiguous, developer-focused string representations, ideally evaluable. Implement `__str__()` for human-readable output. `__str__()` defaults to `__repr__()`.
511+
* **F-string Debug:** Use `f"{expr=}"` for concise debug printing, showing both the expression and its value.
512+
513+
### Libraries and Tools
514+
515+
* **`collections.Counter`:** Use for efficiently counting hashable objects in an iterable.
516+
* **`collections.defaultdict`:** Useful for avoiding key checks when initializing dictionary values, e.g., appending to lists.
517+
* **`heapq`:** Use `heapq.nlargest()` and `heapq.nsmallest()` for efficiently finding the top/bottom N items. Use `heapq.merge()` to merge multiple sorted iterables.
518+
* **`attrs` / `dataclasses`:** Use these libraries to easily define simple classes with boilerplate methods like `__init__`, `__repr__`, `__eq__`, etc., automatically generated.
519+
* **NumPy:** Use NumPy for efficient array computing, element-wise operations, math functions, filtering, and aggregations on numerical data.
520+
* **Pandas:** When constructing DataFrames row by row, append to a list of dicts and call `pd.DataFrame()` once to avoid inefficient copying. Use `TypedDict` or `dataclasses` for intermediate row data.
521+
* **Flags:** Use libraries like `argparse` or `click` for command-line flag parsing. Access flag values in a type-safe manner.
522+
* **Serialization:** For cross-language serialization, consider JSON (built-in), Protocol Buffers, or msgpack. For Python serialization with validation, use `pydantic` for runtime validation and automatic (de)serialization, or `cattrs` for performance-focused (de)serialization with `dataclasses` or `attrs`.
523+
* **Regular Expressions:** Use `re.VERBOSE` to make complex regexes more readable with whitespace and comments. Choose the right method (`re.search`, `re.fullmatch`). Avoid regexes for simple string checks (`in`, `startswith`, `endswith`). Compile regexes used multiple times with `re.compile()`.
524+
* **Caching:** Use `functools.lru_cache` with care. Prefer immutable return types. Be cautious when memoizing methods, as it can lead to memory leaks if the instance is part of the cache key; consider `functools.cached_property`.
525+
* **Pickle:** Avoid using `pickle` due to security risks and compatibility issues. Prefer JSON, Protocol Buffers, or msgpack for serialization.
526+
* **Multiprocessing:** Be aware of potential issues with `multiprocessing` on some platforms, especially concerning `fork`. Consider alternatives like threads (`concurrent.futures.ThreadPoolExecutor`) or `asyncio` for I/O-bound tasks.
527+
* **Debugging:** Use `IPython.embed()` or `pdb.set_trace()` to drop into an interactive shell for debugging. Use visual debuggers if available. Log with context, including inputs and exception info using `logging.exception()` or `exc_info=True`.
528+
* **Property-Based Testing & Fuzzing:** Use `hypothesis` for property-based testing that generates test cases automatically. For coverage-guided fuzzing, consider `atheris` or `python-afl`.
529+
530+
### Testing
531+
532+
* **Assertions:** Use pytest's native `assert` statements with informative expressions. Pytest automatically provides detailed failure messages showing the values involved. Add custom messages with `assert condition, "helpful message"` when the expression alone isn't clear.
533+
* **Custom Assertions:** Write reusable helper functions (not methods) for repeated complex checks. Use `pytest.fail("message")` to explicitly fail a test with a custom message.
534+
* **Parameterized Tests:** Use `@pytest.mark.parametrize` to reduce duplication when running the same test logic with different inputs. This is more idiomatic than the `parameterized` library.
535+
* **Fixtures:** Use pytest fixtures (with `@pytest.fixture`) for test setup, teardown, and dependency injection. Fixtures are cleaner than class-based setup methods and can be easily shared across tests.
536+
* **Mocking:** Use `mock.create_autospec()` with `spec_set=True` to create mocks that match the original object's interface, preventing typos and API mismatch issues. Use context managers (`with mock.patch(...)`) to manage mock lifecycles and ensure patches are stopped. Prefer injecting dependencies via fixtures over patching.
537+
* **Asserting Mock Calls:** Use `mock.ANY` and other matchers for partial argument matching when asserting mock calls (e.g., `assert_called_once_with`).
538+
* **Temporary Files:** Use pytest's `tmp_path` and `tmp_path_factory` fixtures for creating isolated and automatically cleaned-up temporary files/directories. These are preferred over the `tempfile` module in pytest tests.
539+
* **Avoid Randomness:** Do not use random number generators to create inputs for unit tests. This leads to flaky, hard-to-debug tests. Instead, use deterministic, easy-to-reason-about inputs that cover specific behaviors.
540+
* **Test Invariants:** Focus tests on the invariant behaviors of public APIs, not implementation details.
541+
* **Test Organization:** Prefer simple test functions over class-based tests unless you need to share fixtures across multiple test methods in a class. Use descriptive test names that explain the behavior being tested.
542+
543+
### Error Handling
544+
545+
* **Re-raising Exceptions:** Use a bare `raise` to re-raise the current exception, preserving the original stack trace. Use `raise NewException from original_exception` to chain exceptions, providing context. Use `raise NewException from None` to suppress the original exception's context.
546+
* **Exception Messages:** Always include a descriptive message when raising exceptions.
547+
* **Converting Exceptions to Strings:** `str(e)` can be uninformative. `repr(e)` is often better. For full details including tracebacks and chained exceptions, use functions from the `traceback` module (e.g., `traceback.format_exception(e)`, `traceback.format_exc()`).
548+
* **Terminating Programs:** Use `sys.exit()` for expected terminations. Uncaught non-`SystemExit` exceptions should signal bugs. Avoid functions that cause immediate, unclean exits like `os.abort()`.
549+
* **Returning None:** Be consistent. If a function can return a value, all paths should return a value (use `return None` explicitly). Bare `return` is only for early exit in conceptually void functions (annotated with `-> None`).

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
Important Links:
1818
<a href="https://google.github.io/adk-docs/">Docs</a>,
1919
<a href="https://github.com/google/adk-samples">Samples</a>,
20-
<a href="https://github.com/google/adk-java">Java ADK</a> &
20+
<a href="https://github.com/google/adk-java">Java ADK</a>,
21+
<a href="https://github.com/google/adk-go">Go ADK</a> &
2122
<a href="https://github.com/google/adk-web">ADK Web</a>.
2223
</h3>
2324
</html>

contributing/samples/adk_agent_builder_assistant/tools/cleanup_unused_files.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@
1414

1515
"""Cleanup unused files tool for Agent Builder Assistant."""
1616

17+
from __future__ import annotations
18+
1719
from typing import Any
18-
from typing import Dict
19-
from typing import List
20-
from typing import Optional
2120

2221
from google.adk.tools.tool_context import ToolContext
2322

@@ -26,11 +25,11 @@
2625

2726

2827
async def cleanup_unused_files(
29-
used_files: List[str],
28+
used_files: list[str],
3029
tool_context: ToolContext,
31-
file_patterns: Optional[List[str]] = None,
32-
exclude_patterns: Optional[List[str]] = None,
33-
) -> Dict[str, Any]:
30+
file_patterns: list[str] | None = None,
31+
exclude_patterns: list[str] | None = None,
32+
) -> dict[str, Any]:
3433
"""Identify and optionally delete unused files in project directories.
3534
3635
This tool helps clean up unused tool files when agent configurations change.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Sales Assistant Agent with Context Offloading
2+
3+
This agent acts as a sales assistant, capable of generating and retrieving large
4+
sales reports for different regions (North America, EMEA, APAC).
5+
6+
## The Challenge: Large Context Windows
7+
8+
Storing large pieces of data, like full sales reports, directly in conversation
9+
history consumes valuable LLM context window space. This limits how much
10+
conversation history the model can see, potentially degrading response quality
11+
in longer conversations and increasing token costs.
12+
13+
## The Solution: Context Offloading with Artifacts
14+
15+
This agent demonstrates how to use ADK's artifact feature to offload large data
16+
from the main conversation context, while still making it available to the agent
17+
on-demand. Large reports are generated by the `query_large_data` tool but are
18+
immediately saved as artifacts instead of being returned in the function call
19+
response. This keeps the turn events small, saving context space.
20+
21+
### How it Works
22+
23+
1. **Saving Artifacts**: When the user asks for a sales report (e.g., "Get EMEA
24+
sales report"), the `query_large_data` tool is called. It generates a mock
25+
report, saves it as an artifact (`EMEA_sales_report_q3_2025.txt`), and saves
26+
a brief description in the artifact's metadata (e.g., `{'summary': 'Sales
27+
report for EMEA Q3 2025'}`). The tool returns only a confirmation message to
28+
the agent, not the large report itself.
29+
2. **Immediate Loading**: The `QueryLargeDataTool` then runs its
30+
`process_llm_request` hook. It detects that `query_large_data` was just
31+
called, loads the artifact that was just saved, and injects its content into
32+
the *next* request to the LLM. This makes the report data available
33+
immediately, allowing the agent to summarize it or answer questions in the
34+
same turn, as seen in the logs. This artifact is only appended for that
35+
round and not saved to session. For furtuer rounds of conversation, it will
36+
be removed from context.
37+
3. **Loading on Demand**: The `CustomLoadArtifactsTool` enhances the default
38+
`load_artifacts` behavior.
39+
* It reads the `summary` metadata from all available artifacts and includes
40+
these summaries in the instructions sent to the LLM (e.g., `You have
41+
access to artifacts: ["APAC_sales_report_q3_2025.txt: Sales report for
42+
APAC Q3 2025", ...]`). This lets the agent know *what* data is
43+
available in artifacts, without having to load the full content.
44+
* It instructs the agent to use data from the most recent turn if
45+
available, but to call `load_artifacts` if it needs to access data from
46+
an *older* turn that is no longer in the immediate context (e.g., if
47+
comparing North America data after having discussed EMEA and APAC).
48+
* When `load_artifacts` is called, this tool intercepts it and injects the
49+
requested artifact content into the LLM request.
50+
* Note that artifacts are never saved to session.
51+
52+
This pattern ensures that large data is only loaded into the LLM's context
53+
window when it is immediately relevant—either just after being generated or when
54+
explicitly requested later—thereby managing context size more effectively.
55+
56+
### How to Run
57+
58+
```bash
59+
adk web
60+
```
61+
62+
Then, ask the agent:
63+
64+
* "Hi, help me query the North America sales report"
65+
* "help me query EMEA and APAC sales report"
66+
* "Summarize sales report for North America?"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent

0 commit comments

Comments
 (0)