Skip to content

Commit 7c76c8e

Browse files
committed
add examples and lib
1 parent deaa8ed commit 7c76c8e

13 files changed

+909
-3
lines changed

.devcontainer/Dockerfile

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
ARG VARIANT="3.9"
2-
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
1+
FROM mcr.microsoft.com/devcontainers/python:3.12
32

43
USER vscode
54

65
RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash
76
ENV PATH=/home/vscode/.rye/shims:$PATH
87

9-
RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc
8+
RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc

.devcontainer/devcontainer.json

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
}
2525
}
2626
}
27+
},
28+
"features": {
29+
"ghcr.io/devcontainers/features/node:1": {}
2730
}
2831

2932
// Features to add to the dev container. More info: https://containers.dev/features.

examples/anthropic_tool_use.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python
2+
3+
from __future__ import annotations
4+
5+
from typing import cast
6+
7+
from anthropic import Anthropic
8+
from anthropic.types import ToolParam, MessageParam
9+
10+
import gitpod.lib as util
11+
from gitpod import AsyncGitpod
12+
from gitpod.types.environment_initializer_param import Spec
13+
14+
gpclient = AsyncGitpod()
15+
llmclient = Anthropic()
16+
17+
user_message: MessageParam = {
18+
"role": "user",
19+
"content": "What is the test coverage for this repository: https://github.com/gitpod-io/gitpod-sdk-go",
20+
}
21+
tools: list[ToolParam] = [
22+
{
23+
"name": "create_environment",
24+
"description": "Create a new environment for a given context URL. This will create a new environment and return the ID of the environment.",
25+
"input_schema": {
26+
"type": "object",
27+
"properties": {"context_url": {"type": "string"}},
28+
},
29+
},
30+
{
31+
"name": "execute_command",
32+
"description": "Execute a command in a given environment ID. This will execute the command in the given environment and return the output of the command.",
33+
"input_schema": {
34+
"type": "object",
35+
"properties": {"environment_id": {"type": "string"}, "command": {"type": "string"}},
36+
},
37+
},
38+
]
39+
40+
async def create_environment(args: dict[str, str], cleanup: util.Disposables) -> str:
41+
env_class = await util.find_most_used_environment_class(gpclient)
42+
if not env_class:
43+
raise Exception("No environment class found. Please create one first.")
44+
env_class_id = env_class.id
45+
assert env_class_id is not None
46+
47+
environment = (await gpclient.environments.create(
48+
spec={
49+
"desired_phase": "ENVIRONMENT_PHASE_RUNNING",
50+
"content": {
51+
"initializer": {"specs": [Spec(
52+
context_url={
53+
"url": args["context_url"]
54+
}
55+
)]},
56+
},
57+
"machine": {"class": env_class_id},
58+
}
59+
)).environment
60+
assert environment is not None
61+
environment_id = environment.id
62+
assert environment_id is not None
63+
cleanup.add(lambda: asyncio.run(gpclient.environments.delete(environment_id=environment_id)))
64+
65+
print(f"\nCreated environment: {environment_id} - waiting for it to be ready...")
66+
await util.wait_for_environment_ready(gpclient, environment_id)
67+
print(f"\nEnvironment is ready: {environment_id}")
68+
return environment_id
69+
70+
async def execute_command(args: dict[str, str]) -> str:
71+
lines_iter = await util.run_command(gpclient, args["environment_id"], args["command"])
72+
lines: list[str] = []
73+
async for line in lines_iter:
74+
lines.append(line)
75+
return "\n".join(lines)
76+
77+
async def main(cleanup: util.Disposables) -> None:
78+
messages = [user_message]
79+
while True:
80+
message = llmclient.messages.create(
81+
model="claude-3-5-sonnet-latest",
82+
max_tokens=1024,
83+
messages=messages,
84+
tools=tools,
85+
)
86+
print(f"\nResponse: {message.model_dump_json(indent=2)}")
87+
88+
if message.stop_reason != "tool_use":
89+
print(f"\nFinal response reached! {message.model_dump_json(indent=2)}")
90+
break
91+
92+
messages.extend([
93+
{"role": message.role, "content": message.content}
94+
])
95+
96+
# Handle all tool calls in this response
97+
for tool in (c for c in message.content if c.type == "tool_use"):
98+
try:
99+
if tool.name == "create_environment":
100+
args = cast(dict[str, str], tool.input)
101+
environment_id = await create_environment(args, cleanup)
102+
messages.append({
103+
"role": "user",
104+
"content": [{
105+
"type": "tool_result",
106+
"tool_use_id": tool.id,
107+
"content": [{"type": "text", "text": f"The environment ID is {environment_id}"}],
108+
}],
109+
})
110+
elif tool.name == "execute_command":
111+
args = cast(dict[str, str], tool.input)
112+
output = await execute_command(args)
113+
messages.append({
114+
"role": "user",
115+
"content": [{
116+
"type": "tool_result",
117+
"tool_use_id": tool.id,
118+
"content": [{"type": "text", "text": output}],
119+
}],
120+
})
121+
else:
122+
raise Exception(f"Unknown tool: {tool.name}")
123+
except Exception as e:
124+
messages.append({
125+
"role": "user",
126+
"content": [{
127+
"type": "tool_result",
128+
"tool_use_id": tool.id,
129+
"is_error": True,
130+
"content": [{"type": "text", "text": f"Error: {e}"}],
131+
}],
132+
})
133+
134+
print("\nFinal response reached!")
135+
136+
if __name__ == "__main__":
137+
import asyncio
138+
disposables = util.Disposables()
139+
with disposables:
140+
asyncio.run(main(disposables))

examples/fs_access.py

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
import asyncio
5+
from io import StringIO
6+
7+
import paramiko
8+
9+
import gitpod.lib as util
10+
from gitpod import AsyncGitpod
11+
from gitpod.types.environment_spec_param import EnvironmentSpecParam
12+
from gitpod.types.environment_initializer_param import Spec
13+
14+
15+
# Examples:
16+
# - ./examples/fs_access.py
17+
# - ./examples/fs_access.py https://github.com/gitpod-io/empty
18+
async def main(cleanup: util.Disposables) -> None:
19+
client = AsyncGitpod()
20+
21+
context_url = sys.argv[1] if len(sys.argv) > 1 else None
22+
23+
env_class = await util.find_most_used_environment_class(client)
24+
if not env_class:
25+
print("Error: No environment class found. Please create one first.")
26+
sys.exit(1)
27+
print(f"Found environment class: {env_class.display_name} ({env_class.description})")
28+
env_class_id = env_class.id
29+
assert env_class_id is not None
30+
31+
print("Generating SSH key pair")
32+
key = paramiko.RSAKey.generate(2048)
33+
private_key_file = StringIO()
34+
key.write_private_key(private_key_file)
35+
private_key_file.seek(0) # Reset position to start
36+
public_key = f"{key.get_name()} {key.get_base64()}"
37+
38+
print("Creating environment with SSH access")
39+
key_id = "fs-access-example"
40+
spec: EnvironmentSpecParam = {
41+
"desired_phase": "ENVIRONMENT_PHASE_RUNNING",
42+
"machine": {"class": env_class_id},
43+
"ssh_public_keys": [{
44+
"id": key_id,
45+
"value": public_key
46+
}]
47+
}
48+
if context_url:
49+
spec["content"] = {
50+
"initializer": {"specs": [Spec(
51+
context_url={
52+
"url": context_url
53+
}
54+
)]}
55+
}
56+
57+
print("Creating environment")
58+
environment = (await client.environments.create(spec=spec)).environment
59+
assert environment is not None
60+
environment_id = environment.id
61+
assert environment_id is not None
62+
cleanup.add(lambda: asyncio.run(client.environments.delete(environment_id=environment_id)))
63+
64+
env = util.EnvironmentState(client, environment_id)
65+
cleanup.add(lambda: asyncio.run(env.close()))
66+
67+
print("Waiting for environment to be running")
68+
await env.wait_until_running()
69+
70+
print("Waiting for SSH key to be applied")
71+
await env.wait_for_ssh_key_applied(key_id=key_id, key_value=public_key)
72+
73+
print("Waiting for SSH URL")
74+
ssh_url = await env.wait_for_ssh_url()
75+
76+
print(f"Setting up SSH connection to {ssh_url}")
77+
# Parse ssh://username@host:port format
78+
url_parts = ssh_url.split('://')[-1]
79+
username, rest = url_parts.split('@')
80+
host, port_str = rest.split(':')
81+
port = int(port_str)
82+
83+
ssh = paramiko.SSHClient()
84+
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
85+
ssh.connect(
86+
hostname=host,
87+
port=port,
88+
username=username,
89+
pkey=key
90+
)
91+
cleanup.add(lambda: ssh.close())
92+
93+
print("Creating SFTP client")
94+
sftp = ssh.open_sftp()
95+
cleanup.add(lambda: sftp.close())
96+
97+
print("Writing test file")
98+
test_content = "Hello from Gitpod Python SDK!"
99+
with sftp.file('test.txt', 'w') as f:
100+
f.write(test_content)
101+
102+
with sftp.file('test.txt', 'r') as f:
103+
content = f.read()
104+
print(f"File content: {content.decode()}")
105+
106+
if __name__ == "__main__":
107+
disposables = util.Disposables()
108+
with disposables:
109+
asyncio.run(main(disposables))

examples/run_command.py

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
import asyncio
5+
6+
import gitpod.lib as util
7+
from gitpod import AsyncGitpod
8+
from gitpod.types.environment_spec_param import EnvironmentSpecParam
9+
from gitpod.types.environment_initializer_param import Spec
10+
11+
12+
# Examples:
13+
# - ./examples/run_command.py 'echo "Hello World!"'
14+
# - ./examples/run_command.py 'echo "Hello World!"' https://github.com/gitpod-io/empty
15+
async def main(cleanup: util.Disposables) -> None:
16+
client = AsyncGitpod()
17+
18+
if len(sys.argv) < 2:
19+
print("Usage: ./examples/run_command.py '<COMMAND>' [CONTEXT_URL]")
20+
sys.exit(1)
21+
22+
command = sys.argv[1]
23+
context_url = sys.argv[2] if len(sys.argv) > 2 else None
24+
25+
env_class = await util.find_most_used_environment_class(client)
26+
if not env_class:
27+
print("Error: No environment class found. Please create one first.")
28+
sys.exit(1)
29+
print(f"Found environment class: {env_class.display_name} ({env_class.description})")
30+
env_class_id = env_class.id
31+
assert env_class_id is not None
32+
33+
spec: EnvironmentSpecParam = {
34+
"desired_phase": "ENVIRONMENT_PHASE_RUNNING",
35+
"machine": {"class": env_class_id},
36+
}
37+
if context_url:
38+
spec["content"] = {
39+
"initializer": {"specs": [Spec(
40+
context_url={
41+
"url": context_url
42+
}
43+
)]}
44+
}
45+
46+
print("Creating environment")
47+
environment = (await client.environments.create(spec=spec)).environment
48+
assert environment is not None
49+
environment_id = environment.id
50+
assert environment_id is not None
51+
cleanup.add(lambda: asyncio.run(client.environments.delete(environment_id=environment_id)))
52+
53+
print("Waiting for environment to be ready")
54+
await util.wait_for_environment_ready(client, environment_id)
55+
56+
print("Running command")
57+
lines = await util.run_command(client, environment_id, command)
58+
async for line in lines:
59+
print(line)
60+
61+
if __name__ == "__main__":
62+
disposables = util.Disposables()
63+
with disposables:
64+
asyncio.run(main(disposables))

0 commit comments

Comments
 (0)