Skip to content

Commit

Permalink
Merge pull request #1266 from basetenlabs/bump-version-0.9.55
Browse files Browse the repository at this point in the history
Release 0.9.55
  • Loading branch information
marius-baseten authored Dec 5, 2024
2 parents 3a560a5 + d4b1656 commit e8e1664
Show file tree
Hide file tree
Showing 60 changed files with 2,727 additions and 1,151 deletions.
43 changes: 5 additions & 38 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ jobs:
lfs: true
- uses: ./.github/actions/setup-python/
- run: poetry install --with=dev,dev-server --extras=all
- run: poetry run pre-commit run --all-files
- name: pre-commit
run: poetry run pre-commit run --all-files
env:
SKIP: ruff
- run: |
SKIP: ruff,ruff-format # In CI run ruff separately to only check, not fix.
- name: ruff check
run: |
poetry run ruff check .
poetry run ruff format . --check
Expand Down Expand Up @@ -49,38 +51,3 @@ jobs:
with:
use-verbose-mode: "yes"
folder-path: "docs"

enforce-chains-example-docs-sync:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
with:
lfs: true
fetch-depth: 2

- name: Fetch main branch
run: git fetch origin main

- name: Check if chains examples were modified
id: check_files
run: |
if git diff --name-only origin/main | grep -q '^truss-chains/examples/.*'; then
echo "chains_docs_update_needed=true" >> $GITHUB_ENV
echo "Chains examples were modified."
else
echo "chains_docs_update_needed=false" >> $GITHUB_ENV
echo "Chains examples were not modified."
echo "::notice file=truss-chains/examples/::Chains examples not modified."
fi
- name: Enforce acknowledgment in PR description
if: env.chains_docs_update_needed == 'true'
env:
DESCRIPTION: ${{ github.event.pull_request.body }}
run: |
if [[ "$DESCRIPTION" != *"UPDATE_DOCS=done"* && "$DESCRIPTION" != *"UPDATE_DOCS=not_needed"* ]]; then
echo "::error file=truss-chains/examples/::Chains examples were modified and ack not found in PR description. Verify whether docs need to be update (https://github.com/basetenlabs/docs.baseten.co/tree/main/chains) and add an ack tag `UPDATE_DOCS={done|not_needed}` to the PR description."
exit 1
else
echo "::notice file=truss-chains/examples/::Chains examples modified and ack found int PR description."
fi
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
fail_fast: true
# Check & fix all locally: `pre-commit run --files $(git diff --name-only HEAD)`.
fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
Expand Down
699 changes: 367 additions & 332 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "truss"
version = "0.9.53"
version = "0.9.55"
description = "A seamless bridge from model development to model delivery"
license = "MIT"
readme = "README.md"
Expand Down Expand Up @@ -84,6 +84,7 @@ aiohttp = { version = "^3.10.10", optional = false }
blake3 = { version = "^0.3.3", optional = false }
boto3 = { version = "^1.34.85", optional = false }
click = { version = "^8.0.3", optional = false }
fastapi = { version =">=0.109.1", optional = false }
google-cloud-storage = { version = "2.10.0", optional = false }
httpx = { version = ">=0.24.1", optional = false }
inquirerpy = { version = "^0.3.4", optional = false }
Expand Down
7 changes: 2 additions & 5 deletions truss-chains/examples/audio-transcription/transcribe.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ class DeployedWhisper(chains.StubBase):
async def run_remote(
self, whisper_input: data_types.WhisperInput
) -> data_types.WhisperResult:
resp = await self._remote.predict_async(
json_payload={"whisper_input": whisper_input.model_dump()},
)
return data_types.WhisperResult.parse_obj(resp)
return await self.predict_async(whisper_input, data_types.WhisperResult)


class MacroChunkWorker(chains.ChainletBase):
Expand Down Expand Up @@ -93,7 +90,7 @@ async def run_remote(
t1 = time.time()
return data_types.SegmentList(
segments=segments,
chunk_info=macro_chunk.copy(update={"processing_duration": t1 - t0}),
chunk_info=macro_chunk.model_copy(update={"processing_duration": t1 - t0}),
)


Expand Down
92 changes: 92 additions & 0 deletions truss-chains/examples/numpy_and_binary/chain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import numpy as np
import pydantic

import truss_chains as chains
from truss_chains import pydantic_numpy


class DataModel(pydantic.BaseModel):
msg: str
np_array: pydantic_numpy.NumpyArrayField


class SyncChainlet(chains.ChainletBase):
def run_remote(self, data: DataModel) -> DataModel:
print(data)
return data.model_copy(update={"msg": "From sync"})


class AsyncChainlet(chains.ChainletBase):
async def run_remote(self, data: DataModel) -> DataModel:
print(data)
return data.model_copy(update={"msg": "From async"})


class AsyncChainletNoInput(chains.ChainletBase):
async def run_remote(self) -> DataModel:
data = DataModel(msg="From async no input", np_array=np.full((2, 2), 3))
print(data)
return data


class AsyncChainletNoOutput(chains.ChainletBase):
async def run_remote(self, data: DataModel) -> None:
print(data)


class HostJSON(chains.ChainletBase):
"""Calls various chainlets in JSON mode."""

def __init__(
self,
sync_chainlet=chains.depends(SyncChainlet, use_binary=False),
async_chainlet=chains.depends(AsyncChainlet, use_binary=False),
async_chainlet_no_output=chains.depends(
AsyncChainletNoOutput, use_binary=False
),
async_chainlet_no_input=chains.depends(AsyncChainletNoInput, use_binary=False),
):
self._sync_chainlet = sync_chainlet
self._async_chainlet = async_chainlet
self._async_chainlet_no_output = async_chainlet_no_output
self._async_chainlet_no_input = async_chainlet_no_input

async def run_remote(self) -> tuple[DataModel, DataModel, DataModel]:
a = np.ones((3, 2, 1))
data = DataModel(msg="From Host", np_array=a)
sync_result = self._sync_chainlet.run_remote(data)
print(sync_result)
async_result = await self._async_chainlet.run_remote(data)
print(async_result)
await self._async_chainlet_no_output.run_remote(data)
async_no_input = await self._async_chainlet_no_input.run_remote()
print(async_no_input)
return sync_result, async_result, async_no_input


class HostBinary(chains.ChainletBase):
"""Calls various chainlets in binary mode."""

def __init__(
self,
sync_chainlet=chains.depends(SyncChainlet, use_binary=True),
async_chainlet=chains.depends(AsyncChainlet, use_binary=True),
async_chainlet_no_output=chains.depends(AsyncChainletNoOutput, use_binary=True),
async_chainlet_no_input=chains.depends(AsyncChainletNoInput, use_binary=True),
):
self._sync_chainlet = sync_chainlet
self._async_chainlet = async_chainlet
self._async_chainlet_no_output = async_chainlet_no_output
self._async_chainlet_no_input = async_chainlet_no_input

async def run_remote(self) -> tuple[DataModel, DataModel, DataModel]:
a = np.ones((3, 2, 1))
data = DataModel(msg="From Host", np_array=a)
sync_result = self._sync_chainlet.run_remote(data)
print(sync_result)
async_result = await self._async_chainlet.run_remote(data)
print(async_result)
await self._async_chainlet_no_output.run_remote(data)
async_no_input = await self._async_chainlet_no_input.run_remote()
print(async_no_input)
return sync_result, async_result, async_no_input
4 changes: 2 additions & 2 deletions truss-chains/examples/rag/rag_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ async def run_remote(self, new_bio: str, bios: list[str]) -> str:
f"{PERSON_MATCHING_PROMPT}\nPerson you're matching: {new_bio}\n"
f"People from database: {bios_info}"
)
resp = await self._remote.predict_async(
json_payload={
resp = await self.predict_async(
{
"messages": [{"role": "user", "content": prompt}],
"stream": False,
"max_new_tokens": 32,
Expand Down
107 changes: 107 additions & 0 deletions truss-chains/examples/streaming/streaming_chain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import asyncio
import time
from typing import AsyncIterator

import pydantic

import truss_chains as chains
from truss_chains import streaming


class Header(pydantic.BaseModel):
time: float
msg: str


class MyDataChunk(pydantic.BaseModel):
words: list[str]


class Footer(pydantic.BaseModel):
time: float
duration_sec: float
msg: str


class ConsumerOutput(pydantic.BaseModel):
header: Header
chunks: list[MyDataChunk]
footer: Footer
strings: str


STREAM_TYPES = streaming.stream_types(
MyDataChunk, header_type=Header, footer_type=Footer
)


class Generator(chains.ChainletBase):
"""Example that streams fully structured pydantic items with header and footer."""

async def run_remote(self) -> AsyncIterator[bytes]:
print("Entering Generator")
streamer = streaming.stream_writer(STREAM_TYPES)
header = Header(time=time.time(), msg="Start.")
yield streamer.yield_header(header)
for i in range(1, 5):
data = MyDataChunk(
words=[chr(x + 70) * x for x in range(1, i + 1)],
)
print("Yield")
yield streamer.yield_item(data)
await asyncio.sleep(0.05)

end_time = time.time()
footer = Footer(time=end_time, duration_sec=end_time - header.time, msg="Done.")
yield streamer.yield_footer(footer)
print("Exiting Generator")


class StringGenerator(chains.ChainletBase):
"""Minimal streaming example with strings (e.g. for raw LLM output)."""

async def run_remote(self) -> AsyncIterator[str]:
# Note: the "chunk" boundaries are lost, when streaming raw strings. You must
# add spaces and linebreaks to the items yourself..
yield "First "
yield "second "
yield "last."


class Consumer(chains.ChainletBase):
"""Consume that reads the raw streams and parses them."""

def __init__(
self,
generator=chains.depends(Generator),
string_generator=chains.depends(StringGenerator),
):
self._generator = generator
self._string_generator = string_generator

async def run_remote(self) -> ConsumerOutput:
print("Entering Consumer")
reader = streaming.stream_reader(STREAM_TYPES, self._generator.run_remote())
print("Consuming...")
header = await reader.read_header()
chunks = []
async for data in reader.read_items():
print(f"Read: {data}")
chunks.append(data)

footer = await reader.read_footer()
strings = []
async for part in self._string_generator.run_remote():
strings.append(part)

print("Exiting Consumer")
return ConsumerOutput(
header=header, chunks=chunks, footer=footer, strings="".join(strings)
)


if __name__ == "__main__":
with chains.run_local():
chain = Consumer()
result = asyncio.run(chain.run_remote())
print(result)
Loading

0 comments on commit e8e1664

Please sign in to comment.