Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 36 additions & 24 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
[tool.black]
line-length = 88
target-version = ['py38', 'py39', 'py310']
include = '\.pyi?$'
exclude = '''
/(
\.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
)/
'''
[project]
name = "google-adk-samples"
version = "0.1.0"
requires-python = ">=3.10"

[tool.ruff]
line-length = 80
target-version = "py310"

[tool.ruff.lint]
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"C", # flake8-comprehension
"PL", # Pylint rules
"B", # flake8-bugbear
"UP", # pyupgrade
"RUF" # ruff specific rules
]
ignore = [
"E501", # Ignore line length for long LLM prompts
"PLR0913" # Allow agents to have many arguments if needed
]

[tool.ruff.lint.per-file-ignores]
# Global rule: ADK agents use __init__.py to expose their logic
"__init__.py" = ["F401"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

[tool.codespell]
ignore-words-list = "rouge"
skip = "./locust_env/*,uv.lock,.venv,./frontend,**/*.ipynb"

[tool.isort]
profile = "black"
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
line_length = 88
7 changes: 6 additions & 1 deletion python/agents/short-movie-agents/app/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@
model=MODEL,
description=(DESCRIPTION),
instruction=load_prompt_from_file("director_agent.txt"),
sub_agents=[story_agent, screenplay_agent, storyboard_agent, video_agent],
sub_agents=[
story_agent,
screenplay_agent,
storyboard_agent,
video_agent,
],
)
logger.info(f"✅ Agent '{root_agent.name}' created using model '{MODEL}'.")
else:
Expand Down
4 changes: 3 additions & 1 deletion python/agents/short-movie-agents/app/screenplay_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
instruction=load_prompt_from_file("screenplay_agent.txt"),
output_key="screenplay",
)
logger.info(f"✅ Agent '{screenplay_agent.name}' created using model '{MODEL}'.")
logger.info(
f"✅ Agent '{screenplay_agent.name}' created using model '{MODEL}'."
)
except Exception as e:
logger.error(
f"❌ Could not create Screenplay agent. Check API Key ({MODEL}). Error: {e}"
Expand Down
4 changes: 3 additions & 1 deletion python/agents/short-movie-agents/app/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
logging_client = google_cloud_logging.Client()
logger = logging_client.logger(__name__)
allow_origins = (
os.getenv("ALLOW_ORIGINS", "").split(",") if os.getenv("ALLOW_ORIGINS") else None
os.getenv("ALLOW_ORIGINS", "").split(",")
if os.getenv("ALLOW_ORIGINS")
else None
)

bucket_name = f"gs://{project_id}-short-movie-agents-logs-data"
Expand Down
8 changes: 6 additions & 2 deletions python/agents/short-movie-agents/app/story_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@

try:
MODEL = "gemini-2.5-flash"
DESCRIPTION = "An agent that generates a short, engaging campfire story for scouts."
DESCRIPTION = (
"An agent that generates a short, engaging campfire story for scouts."
)
story_agent = Agent(
model=MODEL,
name="story_agent",
description=load_prompt_from_file("story_agent_desc.txt"),
instruction=load_prompt_from_file("story_agent.txt"),
output_key="story",
)
logging.info(f"✅ Agent '{story_agent.name}' created using model '{MODEL}'.")
logging.info(
f"✅ Agent '{story_agent.name}' created using model '{MODEL}'."
)
except Exception as e:
logging.error(
f"❌ Could not create Story agent. Check API Key ({MODEL}). Error: {e}"
Expand Down
8 changes: 6 additions & 2 deletions python/agents/short-movie-agents/app/storyboard_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def storyboard_generate(
AUTHORIZED_URI = "https://storage.mtls.cloud.google.com/"

# Actual image generation
logger.info(f"Generating image for scene {scene_number} with prompt: {prompt}")
logger.info(
f"Generating image for scene {scene_number} with prompt: {prompt}"
)
response = generation_model.generate_images(
prompt=prompt,
number_of_images=1,
Expand Down Expand Up @@ -108,7 +110,9 @@ def storyboard_generate(
output_key="storyboard",
tools=[storyboard_generate],
)
logger.info(f"✅ Agent '{storyboard_agent.name}' created using model '{MODEL}'.")
logger.info(
f"✅ Agent '{storyboard_agent.name}' created using model '{MODEL}'."
)
except Exception as e:
logger.error(
f"❌ Could not create Storyboard agent. Check API Key ({MODEL}). Error: {e}"
Expand Down
6 changes: 4 additions & 2 deletions python/agents/short-movie-agents/app/utils/gcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@

import logging

import google.cloud.storage as storage
from google.api_core import exceptions
from google.cloud import storage


def create_bucket_if_not_exists(bucket_name: str, project: str, location: str) -> None:
def create_bucket_if_not_exists(
bucket_name: str, project: str, location: str
) -> None:
"""Creates a new bucket if it doesn't already exist.

Args:
Expand Down
10 changes: 7 additions & 3 deletions python/agents/short-movie-agents/app/utils/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
from collections.abc import Sequence
from typing import Any

import google.cloud.storage as storage
from google.cloud import logging as google_cloud_logging
from google.cloud import storage
from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExportResult
Expand Down Expand Up @@ -56,8 +56,12 @@ def __init__(
project=self.project_id
)
self.logger = self.logging_client.logger(__name__)
self.storage_client = storage_client or storage.Client(project=self.project_id)
self.bucket_name = bucket_name or f"{self.project_id}-test-agent-logs-data"
self.storage_client = storage_client or storage.Client(
project=self.project_id
)
self.bucket_name = (
bucket_name or f"{self.project_id}-test-agent-logs-data"
)
self.bucket = self.storage_client.bucket(self.bucket_name)

def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
Expand Down
8 changes: 6 additions & 2 deletions python/agents/short-movie-agents/app/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def load_prompt_from_file(
instruction = default_instruction
try:
# Construct path relative to the current script file (__file__)
filepath = os.path.join(os.path.dirname(__file__), PROMPTS_PATH, filename)
filepath = os.path.join(
os.path.dirname(__file__), PROMPTS_PATH, filename
)
with open(filepath, encoding="utf-8") as f:
instruction = f.read()
logger.info(f"Successfully loaded instruction from {filename}")
Expand All @@ -36,5 +38,7 @@ def load_prompt_from_file(
f"WARNING: Instruction file not found: {filepath}. Using default."
)
except Exception as e:
logger.error(f"ERROR loading instruction file {filepath}: {e}. Using default.")
logger.error(
f"ERROR loading instruction file {filepath}: {e}. Using default."
)
return instruction
16 changes: 10 additions & 6 deletions python/agents/short-movie-agents/app/video_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@
MODEL = "gemini-2.5-flash"
VIDEO_MODEL = "veo-3.0-generate-preview"
VIDEO_MODEL_LOCATION = "us-central1"
DESCRIPTION = (
"Agent responsible for creating videos based on a screenplay and storyboards"
)
DESCRIPTION = "Agent responsible for creating videos based on a screenplay and storyboards"
ASPECT_RATIO = "16:9"

client = genai.Client(
Expand Down Expand Up @@ -73,14 +71,20 @@ def video_generate(
AUTHORIZED_URI = "https://storage.mtls.cloud.google.com/"

# Extract dialogue from screenplay
dialogue = "\n".join(re.findall(r"^\w+\s*\(.+\)\s*$", screenplay, re.MULTILINE))
dialogue += "\n".join(re.findall(r"^\s{2,}.+$", screenplay, re.MULTILINE))
dialogue = "\n".join(
re.findall(r"^\w+\s*\(.+\)\s*$", screenplay, re.MULTILINE)
)
dialogue += "\n".join(
re.findall(r"^\s{2,}.+$", screenplay, re.MULTILINE)
)

if dialogue:
prompt += f"\n\nAudio:\n{dialogue}"

# Actual video generation
logger.info(f"Generating video for prompt '{prompt}' and image '{image_link}'")
logger.info(
f"Generating video for prompt '{prompt}' and image '{image_link}'"
)

operation = client.models.generate_videos(
model=VIDEO_MODEL,
Expand Down
66 changes: 13 additions & 53 deletions python/agents/short-movie-agents/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,16 @@
# Copyright 2025 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.

[project]
name = "short-movie-agents"
version = "0.3.1"
description = "Demo showcasing how agents can collaborate to build short movies based on user requests."
authors = [
{name = "Remigiusz Samborski"},
]
license = "Apache-2.0"
readme = "README.md"
requires-python = ">=3.13,<3.14"

dependencies = [
"google-adk~=1.14.0",
"google-adk~=1.14.0, <2.0.0",
"opentelemetry-exporter-gcp-trace~=1.9.0",
"google-cloud-logging>=3.12.0",
"google-cloud-aiplatform[evaluation]~=1.116.0",
Expand All @@ -29,48 +19,24 @@ dependencies = [
"psycopg2-binary>=2.9.10",
]

requires-python = ">=3.13,<3.14"


[dependency-groups]
dev = [
"pytest>=8.3.4",
"pytest-asyncio>=0.23.8",
"nest-asyncio>=1.6.0",
"mypy>=1.14.0"
]

[project.optional-dependencies]

jupyter = [
"jupyter~=1.0.0",
]
lint = [
"ruff>=0.4.6",
"mypy~=1.15.0",
"codespell~=2.2.0",
"types-pyyaml~=6.0.12.20240917",
"types-requests~=2.32.0.20240914",
]

[tool.ruff]
line-length = 88
target-version = "py313"

[tool.ruff.lint]
select = [
"E", # pycodestyle
"F", # pyflakes
"W", # pycodestyle warnings
"I", # isort
"C", # flake8-comprehensions
"B", # flake8-bugbear
"UP", # pyupgrade
"RUF", # ruff specific rules
]
ignore = ["E501", "C901", "B006"] # ignore line too long, too complex
extend = "../../../pyproject.toml"

[tool.ruff.lint.isort]
known-first-party = ["app", "frontend"]
known-first-party = ["app"]

[tool.mypy]
disallow_untyped_calls = true
Expand All @@ -90,19 +56,13 @@ disable_error_code = ["misc", "no-untyped-call", "no-any-return"]

exclude = [".venv"]

[tool.codespell]
ignore-words-list = "rouge"
skip = "./locust_env/*,uv.lock,.venv,./frontend,**/*.ipynb"


[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"


[tool.pytest.ini_options]
pythonpath = "."
asyncio_default_fixture_loop_scope = "function"

[tool.hatch.build.targets.wheel]
packages = ["app","frontend"]
packages = ["app"]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Loading