Skip to content

Commit 5ca8b02

Browse files
committed
Merge branch 'main' into images-in-telegram-transport
2 parents 5946ee5 + b4fa569 commit 5ca8b02

8 files changed

Lines changed: 157 additions & 3 deletions

File tree

src/steamship/data/block.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class Block(CamelModel):
4949
tags: Optional[List[Tag]] = []
5050
index_in_file: Optional[int] = Field(alias="index")
5151
mime_type: Optional[MimeTypes]
52+
public_data: bool = False
53+
5254
url: Optional[
5355
str
5456
] = None # Only for creation of blocks; used to fetch content from a public URL.
@@ -94,6 +96,7 @@ def create(
9496
content: Union[str, bytes] = None,
9597
url: Optional[str] = None,
9698
mime_type: Optional[MimeTypes] = None,
99+
public_data: bool = False,
97100
) -> Block:
98101
"""
99102
Create a new Block within a File specified by file_id.
@@ -122,6 +125,7 @@ def create(
122125
"url": url,
123126
"mimeType": mime_type,
124127
"uploadType": upload_type,
128+
"publicData": public_data,
125129
}
126130

127131
file_data = (
@@ -199,6 +203,16 @@ def is_video(self):
199203
"""Return whether this is a video Block."""
200204
return self.mime_type in [MimeTypes.MP4_VIDEO, MimeTypes.WEBM_VIDEO]
201205

206+
@property
207+
def raw_data_url(self) -> Optional[str]:
208+
"""Return a URL at which the data content of this Block can be accessed. If public_data is True,
209+
this content can be accessed without an API key.
210+
"""
211+
if self.client is not None:
212+
return f"{self.client.config.api_base}block/{self.id}/raw"
213+
else:
214+
return None
215+
202216
@property
203217
def chat_role(self) -> Optional[RoleTag]:
204218
return get_tag_value_key(

src/steamship/data/file.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class File(CamelModel):
5555
blocks: List[Block] = []
5656
tags: List[Tag] = []
5757
filename: str = None
58+
public_data: bool = False
5859

5960
class CreateResponse(Response):
6061
data_: Any = None
@@ -115,6 +116,7 @@ def create(
115116
handle: str = None,
116117
blocks: List[Block] = None,
117118
tags: List[Tag] = None,
119+
public_data: bool = False,
118120
) -> Any:
119121

120122
if content is None and blocks is None:
@@ -145,6 +147,7 @@ def create(
145147
"tags": [
146148
tag.dict(by_alias=True, exclude_unset=True, exclude_none=True) for tag in tags or []
147149
],
150+
"publicData": public_data,
148151
}
149152

150153
file_data = (
@@ -209,6 +212,16 @@ def raw(self):
209212
raw_response=True,
210213
)
211214

215+
@property
216+
def raw_data_url(self) -> Optional[str]:
217+
"""Return a URL at which the data content of this File can be accessed. If public_data is True,
218+
this content can be accessed without an API key.
219+
"""
220+
if self.client is not None:
221+
return f"{self.client.config.api_base}file/{self.id}/raw"
222+
else:
223+
return None
224+
212225
def blockify(self, plugin_instance: str = None, wait_on_tasks: List[Task] = None) -> Task:
213226
from steamship.data.operations.blockifier import BlockifyRequest
214227
from steamship.plugin.outputs.block_and_tag_plugin_output import BlockAndTagPluginOutput

src/steamship/data/operations/generator.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ class GenerateRequest(Request):
5151
# create a new file.
5252
output_file_id: Optional[str] = None
5353

54+
# May specify that the output blocks' bytes content should be made public-readable.
55+
# Useful for generating images / audio / etc that will be shared.
56+
# Defaults to False (private) content.
57+
# Requires append_output_to_file to be True.
58+
make_output_public: Optional[bool] = None
59+
5460
# Arbitrary runtime options which may be passed to a generator
5561
options: Optional[dict]
5662

src/steamship/data/plugin/plugin_instance.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def generate(
128128
# url: Optional[str] = None, [Not yet implemented]
129129
append_output_to_file: bool = False,
130130
output_file_id: Optional[str] = None,
131+
make_output_public: Optional[bool] = None,
131132
options: Optional[dict] = None,
132133
) -> Task[GenerateResponse]:
133134
"""See GenerateRequest for description of parameter options"""
@@ -143,6 +144,7 @@ def generate(
143144
# url=url,
144145
append_output_to_file=append_output_to_file,
145146
output_file_id=output_file_id,
147+
make_output_public=make_output_public,
146148
options=options,
147149
)
148150
return self.client.post("plugin/instance/generate", req, expect=GenerateResponse)

src/steamship/utils/repl.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,17 @@ class AgentREPL(SteamshipREPL):
118118
config = None
119119

120120
def __init__(
121-
self, agent_class: Type[AgentService], method: str, client: Optional[Steamship] = None
121+
self,
122+
agent_class: Type[AgentService],
123+
method: str,
124+
agent_package_config: Optional[Dict[str, Any]],
125+
client: Optional[Steamship] = None,
122126
):
123127
super().__init__()
124128
self.agent_class = agent_class
125129
self.method = method
126130
self.client = client or Steamship()
131+
self.config = agent_package_config
127132

128133
def run_with_client(self, client: Steamship):
129134
try:
@@ -136,7 +141,7 @@ def colored(text: str, color: str):
136141
print("Starting REPL for Agent...")
137142
print("If you make code changes, restart this REPL. Press CTRL+C to exit at any time.\n")
138143

139-
agent_service = self.agent_class(client=client)
144+
agent_service = self.agent_class(client=client, config=self.config)
140145

141146
while True:
142147
input_text = input(colored(text="Input: ", color="blue")) # noqa: F821

tests/steamship_tests/data/test_block.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
import requests
23
from steamship_tests import TEST_ASSETS_PATH
34

45
from steamship import MimeTypes
@@ -175,3 +176,40 @@ def test_append_is_present(client: Steamship):
175176
assert len(my_file.blocks) == 0
176177
my_file.append_block(text="first")
177178
assert len(my_file.blocks) == 1
179+
180+
181+
@pytest.mark.usefixtures("client")
182+
def test_append_block_public_data(client: Steamship):
183+
file = File.create(client, blocks=[])
184+
185+
appended_block = Block.create(
186+
client, file_id=file.id, content=bytes("This is a test", "utf-8"), public_data=True
187+
)
188+
assert appended_block.public_data
189+
190+
# Intentionally no API key
191+
response = requests.get(appended_block.raw_data_url)
192+
193+
assert response.text == "This is a test"
194+
assert response.headers["content-type"] == MimeTypes.TXT
195+
196+
197+
@pytest.mark.usefixtures("client")
198+
def test_append_block_private_data(client: Steamship):
199+
file = File.create(client, blocks=[])
200+
201+
appended_block = Block.create(client, file_id=file.id, content=bytes("This is a test", "utf-8"))
202+
assert not appended_block.public_data
203+
204+
# Intentionally no API key
205+
failed_response = requests.get(appended_block.raw_data_url)
206+
assert not failed_response.ok
207+
208+
# Should still be able to get with API key
209+
response = requests.get(
210+
appended_block.raw_data_url,
211+
headers={"Authorization": f"Bearer {client.config.api_key.get_secret_value()}"},
212+
)
213+
214+
assert response.text == "This is a test"
215+
assert response.headers["content-type"] == MimeTypes.TXT

tests/steamship_tests/data/test_file.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,34 @@ def test_append_with_tag(client: Steamship):
351351
assert file.blocks[1].text == "second"
352352
assert len(file.blocks[1].tags) == 1
353353
assert file.blocks[1].tags[0].kind == TagKind.DOCUMENT
354+
355+
356+
@pytest.mark.usefixtures("client")
357+
def test_file_public_data(client: Steamship):
358+
file = File.create(client, content=bytes("This is a test", "utf-8"), public_data=True)
359+
assert file.public_data
360+
361+
# Intentionally no API key
362+
response = requests.get(file.raw_data_url)
363+
364+
assert response.text == "This is a test"
365+
assert response.headers["content-type"] == MimeTypes.TXT
366+
367+
368+
@pytest.mark.usefixtures("client")
369+
def test_append_block_private_data(client: Steamship):
370+
file = File.create(client, content=bytes("This is a test", "utf-8"))
371+
assert not file.public_data
372+
373+
# Intentionally no API key
374+
failed_response = requests.get(file.raw_data_url)
375+
assert not failed_response.ok
376+
377+
# Should still be able to get with API key
378+
response = requests.get(
379+
file.raw_data_url,
380+
headers={"Authorization": f"Bearer {client.config.api_key.get_secret_value()}"},
381+
)
382+
383+
assert response.text == "This is a test"
384+
assert response.headers["content-type"] == MimeTypes.TXT

tests/steamship_tests/plugin/integration/test_e2e_generator.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import pytest
2+
import requests
13
from assets.plugins.generators.test_generator_returns_bytes import TEST_BYTES_STRING
24
from steamship_tests import PLUGINS_PATH, TEST_ASSETS_PATH
35
from steamship_tests.utils.deployables import deploy_plugin
46
from steamship_tests.utils.fixtures import get_steamship_client
57

6-
from steamship import Block, File, MimeTypes, PluginInstance
8+
from steamship import Block, File, MimeTypes, PluginInstance, Steamship
79

810

911
def test_e2e_generator():
@@ -173,3 +175,46 @@ def test_e2e_generate_returning_bytes():
173175
assert persisted_block.file_id is not None
174176
persisted_block_content = persisted_block.raw()
175177
assert persisted_block_content == TEST_BYTES_STRING.encode("utf-8")
178+
179+
180+
@pytest.mark.usefixtures("client")
181+
def test_generate_block_public_data(client: Steamship):
182+
client = get_steamship_client()
183+
plugin_instance = PluginInstance.create(client, plugin_handle="test-image-generator")
184+
generate_task = plugin_instance.generate(
185+
text="This won't be used", append_output_to_file=True, make_output_public=True
186+
)
187+
188+
blocks = generate_task.wait().blocks
189+
assert blocks is not None
190+
assert blocks[0].public_data
191+
192+
# Intentionally no API key
193+
response = requests.get(blocks[0].raw_data_url)
194+
195+
assert response.text == "PRETEND THIS IS THE DATA OF AN IMAGE"
196+
assert response.headers["content-type"] == MimeTypes.PNG
197+
198+
199+
@pytest.mark.usefixtures("client")
200+
def test_generate_block_private_data(client: Steamship):
201+
client = get_steamship_client()
202+
plugin_instance = PluginInstance.create(client, plugin_handle="test-image-generator")
203+
generate_task = plugin_instance.generate(text="This won't be used", append_output_to_file=True)
204+
205+
blocks = generate_task.wait().blocks
206+
assert blocks is not None
207+
assert not blocks[0].public_data
208+
209+
# Intentionally no API key
210+
failed_response = requests.get(blocks[0].raw_data_url)
211+
assert not failed_response.ok
212+
213+
# Should still be able to get with API key
214+
response = requests.get(
215+
blocks[0].raw_data_url,
216+
headers={"Authorization": f"Bearer {client.config.api_key.get_secret_value()}"},
217+
)
218+
219+
assert response.text == "PRETEND THIS IS THE DATA OF AN IMAGE"
220+
assert response.headers["content-type"] == MimeTypes.PNG

0 commit comments

Comments
 (0)