GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix(templates): bound Gemini CLI shutdown and read long stream-json l… · scaleapi/scale-agentex-python@583856c

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • src/agentex/lib/cli/templates
    • default-gemini-cli
      • Dockerfile-uv.j2
      • Dockerfile.j2
      • README.md.j2
      • manifest.yaml.j2
      • project
        • acp.py.j2
    • sync-gemini-cli
      • Dockerfile-uv.j2
      • Dockerfile.j2
      • README.md.j2
      • manifest.yaml.j2
      • project
        • acp.py.j2
    • temporal-gemini-cli
      • Dockerfile-uv.j2
      • Dockerfile.j2
      • manifest.yaml.j2
      • project
        • activities.py.j2
        • run_worker.py.j2
        • workflow.py.j2
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/agentex/lib/cli/templates/default-gemini-cli/Dockerfile-uv.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/default-gemini-cli/Dockerfile-uv.j2
+21-1Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
2020
&& apt-get clean \
2121
&& rm -rf /var/lib/apt/lists/**
2222

23-
# Install the Gemini CLI CLI: the agent shells out to `gemini` on every turn,
23+
# Install the Gemini CLI: the agent shells out to `gemini` on every turn,
2424
# so the binary must be present in the runtime image.
2525
RUN npm install -g @google/gemini-cli
2626

@@ -34,14 +34,34 @@ WORKDIR /app/{{ project_path_from_build_root }}
3434
COPY {{ project_path_from_build_root }}/pyproject.toml ./
3535

3636
# Install dependencies (without project itself, for layer caching)
37+
# Optional private index for Scale-internal packages such as sgp-obs, injected by the
38+
# control-plane broker. Inert unless the secret is present, so local
39+
# builds, CI builds, and agents that never opt in are unaffected.
40+
#
41+
# To opt in, and for why UV_INDEX is pinned to the broker's URL rather than trusting
42+
# the project's, see PRIVATE_INDEX.md in the agentex-sdk CLI templates directory.
3743
RUN --mount=type=cache,target=/root/.cache/uv \
44+
--mount=type=secret,id=codeartifact-pip-conf,required=false \
45+
if [ -s /run/secrets/codeartifact-pip-conf ]; then \
46+
export UV_INDEX="scale-pypi=$(sed -n 's#.*index-url = https://aws:[^@]*@\(.*\)#https://\1#p' /run/secrets/codeartifact-pip-conf | head -1)"; \
47+
export UV_INDEX_SCALE_PYPI_USERNAME=aws; \
48+
export UV_INDEX_SCALE_PYPI_PASSWORD="$(sed -n 's#.*index-url = https://aws:\([^@]*\)@.*#\1#p' /run/secrets/codeartifact-pip-conf \
49+
| python3 -c 'import sys,urllib.parse;print(urllib.parse.unquote(sys.stdin.read().strip()))')"; \
50+
fi; \
3851
uv sync --no-install-project --no-dev
3952

4053
# Copy the project code
4154
COPY {{ project_path_from_build_root }}/project ./project
4255

4356
# Install the project
4457
RUN --mount=type=cache,target=/root/.cache/uv \
58+
--mount=type=secret,id=codeartifact-pip-conf,required=false \
59+
if [ -s /run/secrets/codeartifact-pip-conf ]; then \
60+
export UV_INDEX="scale-pypi=$(sed -n 's#.*index-url = https://aws:[^@]*@\(.*\)#https://\1#p' /run/secrets/codeartifact-pip-conf | head -1)"; \
61+
export UV_INDEX_SCALE_PYPI_USERNAME=aws; \
62+
export UV_INDEX_SCALE_PYPI_PASSWORD="$(sed -n 's#.*index-url = https://aws:\([^@]*\)@.*#\1#p' /run/secrets/codeartifact-pip-conf \
63+
| python3 -c 'import sys,urllib.parse;print(urllib.parse.unquote(sys.stdin.read().strip()))')"; \
64+
fi; \
4565
uv sync --no-dev
4666

4767
ENV PATH="/app/{{ project_path_from_build_root }}/.venv/bin:$PATH"
Collapse file

‎src/agentex/lib/cli/templates/default-gemini-cli/Dockerfile.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/default-gemini-cli/Dockerfile.j2
+13-2Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
2020
&& apt-get clean \
2121
&& rm -rf /var/lib/apt/lists/*
2222

23-
# Install the Gemini CLI CLI: the agent shells out to `gemini` on every turn,
23+
# Install the Gemini CLI: the agent shells out to `gemini` on every turn,
2424
# so the binary must be present in the runtime image.
2525
RUN npm install -g @google/gemini-cli
2626

@@ -33,8 +33,19 @@ COPY {{ project_path_from_build_root }}/requirements.txt /app/{{ project_path_fr
3333

3434
WORKDIR /app/{{ project_path_from_build_root }}
3535

36+
# Optional private index for Scale-internal packages such as sgp-obs, injected by the
37+
# control-plane broker. Inert unless the secret is present.
38+
#
39+
# This variant installs from requirements.txt, so there is no pyproject.toml for uv to
40+
# read a named index out of; the credentialed URL is used directly and is deliberately
41+
# NOT decoded. See PRIVATE_INDEX.md in the agentex-sdk CLI templates directory.
42+
#
3643
# Install the required Python packages
37-
RUN uv pip install --system -r requirements.txt
44+
RUN --mount=type=secret,id=codeartifact-pip-conf,required=false \
45+
if [ -s /run/secrets/codeartifact-pip-conf ]; then \
46+
export UV_DEFAULT_INDEX="$(sed -n 's#^[[:space:]]*index-url[[:space:]]*=[[:space:]]*##p' /run/secrets/codeartifact-pip-conf | head -1)"; \
47+
fi; \
48+
uv pip install --system -r requirements.txt
3849

3950
# Copy the project code
4051
COPY {{ project_path_from_build_root }}/project /app/{{ project_path_from_build_root }}/project
Collapse file

‎src/agentex/lib/cli/templates/default-gemini-cli/README.md.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/default-gemini-cli/README.md.j2
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# {{ agent_name }} - AgentEx Async Gemini CLI Agent
22

33
This template builds an **asynchronous** (non-Temporal) agent that drives the
4-
**Gemini CLI CLI** through the unified harness surface on AgentEx:
4+
**Gemini CLI** through the unified harness surface on AgentEx:
55
- Spawns `gemini -p "<prompt>" --output-format stream-json` as a local subprocess
66
- Wraps the CLI's stdout stream in a `GeminiCliTurn`
77
- Delivers canonical `StreamTaskMessage*` events via `UnifiedEmitter.auto_send_turn`
@@ -40,7 +40,7 @@ agentex agents run --manifest manifest.yaml
4040

4141
### Async ACP with the harness
4242
The async ACP model streams events over Redis instead of an HTTP response. The
43-
`@acp.on_task_event_send` handler spawns the Gemini CLI CLI and pushes the
43+
`@acp.on_task_event_send` handler spawns the Gemini CLI and pushes the
4444
harness events to the task stream.
4545

4646
### The unified harness surface
Collapse file

‎src/agentex/lib/cli/templates/default-gemini-cli/manifest.yaml.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/default-gemini-cli/manifest.yaml.j2
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ agent:
7676
# Maps Kubernetes secrets to environment variables
7777
# Common credentials include:
7878
credentials:
79-
# The Gemini CLI CLI authenticates with GEMINI_API_KEY (LITELLM_API_KEY
79+
# The Gemini CLI authenticates with GEMINI_API_KEY (LITELLM_API_KEY
8080
# is not read by the `gemini` subprocess this agent spawns).
8181
- env_var_name: GEMINI_API_KEY
8282
secret_name: gemini-api-key
Collapse file

‎src/agentex/lib/cli/templates/default-gemini-cli/project/acp.py.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/default-gemini-cli/project/acp.py.j2
+17-1Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ from agentex.lib.core.tracing.tracing_processor_manager import add_tracing_proce
3333

3434
logger = make_logger(__name__)
3535

36+
STDOUT_LINE_LIMIT = 8 * 1024 * 1024
37+
TERMINATE_TIMEOUT_SECONDS = 5.0
38+
3639
add_tracing_processor_config(
3740
SGPTracingProcessorConfig(
3841
sgp_api_key=os.environ.get("SGP_API_KEY", ""),
@@ -52,6 +55,11 @@ async def _spawn_gemini(prompt: str) -> AsyncIterator[str]:
5255

5356
Injectable seam: tests can monkeypatch this with a fake async iterator of
5457
pre-recorded lines so no real CLI invocation is needed offline.
58+
59+
Lines up to ``STDOUT_LINE_LIMIT`` are read whole: a ``tool_result`` that
60+
echoes a file is a single stream-json line, often past asyncio's 64 KiB
61+
default. If the consumer stops early, the CLI gets SIGTERM and, after
62+
``TERMINATE_TIMEOUT_SECONDS``, SIGKILL, so cancellation cannot hang.
5563
"""
5664
# The prompt goes in via ``-p`` (argv). Stdin is closed on purpose: in
5765
# non-interactive mode the CLI reads stdin to EOF and appends it to the
@@ -67,6 +75,7 @@ async def _spawn_gemini(prompt: str) -> AsyncIterator[str]:
6775
stdin=asyncio.subprocess.DEVNULL,
6876
stdout=asyncio.subprocess.PIPE,
6977
stderr=asyncio.subprocess.PIPE,
78+
limit=STDOUT_LINE_LIMIT,
7079
)
7180
assert proc.stdout is not None
7281

@@ -123,7 +132,14 @@ async def _spawn_gemini(prompt: str) -> AsyncIterator[str]:
123132
proc.terminate()
124133
except ProcessLookupError:
125134
pass
126-
await proc.wait()
135+
try:
136+
await asyncio.wait_for(proc.wait(), timeout=TERMINATE_TIMEOUT_SECONDS)
137+
except TimeoutError:
138+
try:
139+
proc.kill()
140+
except ProcessLookupError:
141+
pass
142+
await proc.wait()
127143

128144

129145
@acp.on_task_create
Collapse file

‎src/agentex/lib/cli/templates/sync-gemini-cli/Dockerfile-uv.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/sync-gemini-cli/Dockerfile-uv.j2
+21-1Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
2020
&& apt-get clean \
2121
&& rm -rf /var/lib/apt/lists/**
2222

23-
# Install the Gemini CLI CLI: the agent shells out to `gemini` on every turn,
23+
# Install the Gemini CLI: the agent shells out to `gemini` on every turn,
2424
# so the binary must be present in the runtime image.
2525
RUN npm install -g @google/gemini-cli
2626

@@ -34,14 +34,34 @@ WORKDIR /app/{{ project_path_from_build_root }}
3434
COPY {{ project_path_from_build_root }}/pyproject.toml ./
3535

3636
# Install dependencies (without project itself, for layer caching)
37+
# Optional private index for Scale-internal packages such as sgp-obs, injected by the
38+
# control-plane broker. Inert unless the secret is present, so local
39+
# builds, CI builds, and agents that never opt in are unaffected.
40+
#
41+
# To opt in, and for why UV_INDEX is pinned to the broker's URL rather than trusting
42+
# the project's, see PRIVATE_INDEX.md in the agentex-sdk CLI templates directory.
3743
RUN --mount=type=cache,target=/root/.cache/uv \
44+
--mount=type=secret,id=codeartifact-pip-conf,required=false \
45+
if [ -s /run/secrets/codeartifact-pip-conf ]; then \
46+
export UV_INDEX="scale-pypi=$(sed -n 's#.*index-url = https://aws:[^@]*@\(.*\)#https://\1#p' /run/secrets/codeartifact-pip-conf | head -1)"; \
47+
export UV_INDEX_SCALE_PYPI_USERNAME=aws; \
48+
export UV_INDEX_SCALE_PYPI_PASSWORD="$(sed -n 's#.*index-url = https://aws:\([^@]*\)@.*#\1#p' /run/secrets/codeartifact-pip-conf \
49+
| python3 -c 'import sys,urllib.parse;print(urllib.parse.unquote(sys.stdin.read().strip()))')"; \
50+
fi; \
3851
uv sync --no-install-project --no-dev
3952

4053
# Copy the project code
4154
COPY {{ project_path_from_build_root }}/project ./project
4255

4356
# Install the project
4457
RUN --mount=type=cache,target=/root/.cache/uv \
58+
--mount=type=secret,id=codeartifact-pip-conf,required=false \
59+
if [ -s /run/secrets/codeartifact-pip-conf ]; then \
60+
export UV_INDEX="scale-pypi=$(sed -n 's#.*index-url = https://aws:[^@]*@\(.*\)#https://\1#p' /run/secrets/codeartifact-pip-conf | head -1)"; \
61+
export UV_INDEX_SCALE_PYPI_USERNAME=aws; \
62+
export UV_INDEX_SCALE_PYPI_PASSWORD="$(sed -n 's#.*index-url = https://aws:\([^@]*\)@.*#\1#p' /run/secrets/codeartifact-pip-conf \
63+
| python3 -c 'import sys,urllib.parse;print(urllib.parse.unquote(sys.stdin.read().strip()))')"; \
64+
fi; \
4565
uv sync --no-dev
4666

4767
ENV PATH="/app/{{ project_path_from_build_root }}/.venv/bin:$PATH"
Collapse file

‎src/agentex/lib/cli/templates/sync-gemini-cli/Dockerfile.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/sync-gemini-cli/Dockerfile.j2
+13-2Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
2020
&& apt-get clean \
2121
&& rm -rf /var/lib/apt/lists/*
2222

23-
# Install the Gemini CLI CLI: the agent shells out to `gemini` on every turn,
23+
# Install the Gemini CLI: the agent shells out to `gemini` on every turn,
2424
# so the binary must be present in the runtime image.
2525
RUN npm install -g @google/gemini-cli
2626

@@ -33,8 +33,19 @@ COPY {{ project_path_from_build_root }}/requirements.txt /app/{{ project_path_fr
3333

3434
WORKDIR /app/{{ project_path_from_build_root }}
3535

36+
# Optional private index for Scale-internal packages such as sgp-obs, injected by the
37+
# control-plane broker. Inert unless the secret is present.
38+
#
39+
# This variant installs from requirements.txt, so there is no pyproject.toml for uv to
40+
# read a named index out of; the credentialed URL is used directly and is deliberately
41+
# NOT decoded. See PRIVATE_INDEX.md in the agentex-sdk CLI templates directory.
42+
#
3643
# Install the required Python packages
37-
RUN uv pip install --system -r requirements.txt
44+
RUN --mount=type=secret,id=codeartifact-pip-conf,required=false \
45+
if [ -s /run/secrets/codeartifact-pip-conf ]; then \
46+
export UV_DEFAULT_INDEX="$(sed -n 's#^[[:space:]]*index-url[[:space:]]*=[[:space:]]*##p' /run/secrets/codeartifact-pip-conf | head -1)"; \
47+
fi; \
48+
uv pip install --system -r requirements.txt
3849

3950
# Copy the project code
4051
COPY {{ project_path_from_build_root }}/project /app/{{ project_path_from_build_root }}/project
Collapse file

‎src/agentex/lib/cli/templates/sync-gemini-cli/README.md.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/sync-gemini-cli/README.md.j2
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# {{ agent_name }} - AgentEx Sync Gemini CLI Agent
22

3-
This template builds a **synchronous** agent that drives the **Gemini CLI CLI**
3+
This template builds a **synchronous** agent that drives the **Gemini CLI**
44
through the unified harness surface on AgentEx:
55
- Spawns `gemini -p "<prompt>" --output-format stream-json` as a local subprocess
66
- Wraps the CLI's stdout stream in a `GeminiCliTurn`
@@ -40,7 +40,7 @@ agentex agents run --manifest manifest.yaml
4040

4141
### Sync ACP with the harness
4242
The sync ACP model uses HTTP request/response. The `@acp.on_message_send`
43-
handler spawns the Gemini CLI CLI and yields the harness events back to the
43+
handler spawns the Gemini CLI and yields the harness events back to the
4444
client as they arrive.
4545

4646
### The unified harness surface
Collapse file

‎src/agentex/lib/cli/templates/sync-gemini-cli/manifest.yaml.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/sync-gemini-cli/manifest.yaml.j2
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ agent:
7575
# Maps Kubernetes secrets to environment variables
7676
# Common credentials include:
7777
credentials:
78-
# The Gemini CLI CLI authenticates with GEMINI_API_KEY (LITELLM_API_KEY
78+
# The Gemini CLI authenticates with GEMINI_API_KEY (LITELLM_API_KEY
7979
# is not read by the `gemini` subprocess this agent spawns).
8080
- env_var_name: GEMINI_API_KEY
8181
secret_name: gemini-api-key
Collapse file

‎src/agentex/lib/cli/templates/sync-gemini-cli/project/acp.py.j2‎

Copy file name to clipboardExpand all lines: src/agentex/lib/cli/templates/sync-gemini-cli/project/acp.py.j2
+17-1Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ from agentex.lib.core.tracing.tracing_processor_manager import add_tracing_proce
3535

3636
logger = make_logger(__name__)
3737

38+
STDOUT_LINE_LIMIT = 8 * 1024 * 1024
39+
TERMINATE_TIMEOUT_SECONDS = 5.0
40+
3841
add_tracing_processor_config(
3942
SGPTracingProcessorConfig(
4043
sgp_api_key=os.environ.get("SGP_API_KEY", ""),
@@ -51,6 +54,11 @@ async def _spawn_gemini(prompt: str) -> AsyncIterator[str]:
5154

5255
This is a seam: tests can replace it with a fake async iterator of
5356
pre-recorded lines so no real CLI invocation is needed offline.
57+
58+
Lines up to ``STDOUT_LINE_LIMIT`` are read whole: a ``tool_result`` that
59+
echoes a file is a single stream-json line, often past asyncio's 64 KiB
60+
default. If the consumer stops early, the CLI gets SIGTERM and, after
61+
``TERMINATE_TIMEOUT_SECONDS``, SIGKILL, so cancellation cannot hang.
5462
"""
5563
# The prompt goes in via ``-p`` (argv). Stdin is closed on purpose: in
5664
# non-interactive mode the CLI reads stdin to EOF and appends it to the
@@ -66,6 +74,7 @@ async def _spawn_gemini(prompt: str) -> AsyncIterator[str]:
6674
stdin=asyncio.subprocess.DEVNULL,
6775
stdout=asyncio.subprocess.PIPE,
6876
stderr=asyncio.subprocess.PIPE,
77+
limit=STDOUT_LINE_LIMIT,
6978
)
7079
assert proc.stdout is not None
7180

@@ -122,7 +131,14 @@ async def _spawn_gemini(prompt: str) -> AsyncIterator[str]:
122131
proc.terminate()
123132
except ProcessLookupError:
124133
pass
125-
await proc.wait()
134+
try:
135+
await asyncio.wait_for(proc.wait(), timeout=TERMINATE_TIMEOUT_SECONDS)
136+
except TimeoutError:
137+
try:
138+
proc.kill()
139+
except ProcessLookupError:
140+
pass
141+
await proc.wait()
126142

127143

128144
@acp.on_message_send

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.