From 6dfb478de94979608d6b5c7657dc6c9cc53ee43f Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 11 Nov 2025 10:37:26 -0600 Subject: [PATCH 1/3] Close #219. ContentPDF now retains url information (if any) and translates to Inspect --- chatlas/_content.py | 7 ++++++- chatlas/_content_pdf.py | 2 +- chatlas/_inspect.py | 19 +++++++++++++------ chatlas/_provider_openai_completions.py | 8 ++++++-- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/chatlas/_content.py b/chatlas/_content.py index be17e775..15f1a77a 100644 --- a/chatlas/_content.py +++ b/chatlas/_content.py @@ -665,12 +665,17 @@ class ContentPDF(Content): Parameters ---------- - value + data The PDF data extracted + filename + The name of the PDF file + url + An optional URL where the PDF can be accessed """ data: bytes filename: str + url: Optional[str] = None content_type: ContentTypeEnum = "pdf" diff --git a/chatlas/_content_pdf.py b/chatlas/_content_pdf.py index 042a7a3c..335e4218 100644 --- a/chatlas/_content_pdf.py +++ b/chatlas/_content_pdf.py @@ -77,7 +77,7 @@ def content_pdf_url(url: str) -> ContentPDF: # apis where they exist. Might need some kind of mutable state so can # record point to uploaded file. data = download_pdf_bytes(url) - return ContentPDF(data=data, filename=unique_pdf_name()) + return ContentPDF(data=data, filename=unique_pdf_name(), url=url) def parse_data_url(url: str) -> tuple[str, str]: diff --git a/chatlas/_inspect.py b/chatlas/_inspect.py index b847349f..2ea40e0c 100644 --- a/chatlas/_inspect.py +++ b/chatlas/_inspect.py @@ -172,8 +172,11 @@ def chatlas_content_as_inspect(content: ContentUnion) -> InspectContent: data_url = f"data:{content.image_content_type};base64,{content.data or ''}" return itool.ContentImage(image=data_url, detail="auto") elif isinstance(content, ContentPDF): + doc = content.url + if doc is None: + doc = f"data:application/pdf;base64,{base64.b64encode(content.data).decode('ascii')}" return itool.ContentDocument( - document=base64.b64encode(content.data).decode("ascii"), + document=doc, mime_type="application/pdf", filename=content.filename, ) @@ -211,13 +214,17 @@ def inspect_content_as_chatlas(content: str | InspectContent) -> Content: image_content_type=content_type, # type: ignore ) if isinstance(content, itool.ContentDocument): + doc = content.document if content.mime_type == "application/pdf": - return ContentPDF( - data=base64.b64decode(content.document), - filename=content.filename, - ) + url = None + data = b"" + if doc.startswith("http://") or doc.startswith("https://"): + url = doc + else: + data = base64.b64decode(doc.replace("data:application/pdf;base64,", "")) + return ContentPDF(data=data, url=url, filename=content.filename) else: - return ContentText(text=content.document) + return ContentText(text=doc) if isinstance(content, itool.ContentData): return ContentJson(value=content.data) raise ValueError( diff --git a/chatlas/_provider_openai_completions.py b/chatlas/_provider_openai_completions.py index 99730a33..87d930ed 100644 --- a/chatlas/_provider_openai_completions.py +++ b/chatlas/_provider_openai_completions.py @@ -56,18 +56,22 @@ def ChatOpenAICompletions( *, + base_url: str = "https://api.openai.com/v1", system_prompt: Optional[str] = None, model: "Optional[ChatModel | str]" = None, api_key: Optional[str] = None, - base_url: str = "https://api.openai.com/v1", seed: int | None | MISSING_TYPE = MISSING, kwargs: Optional["ChatClientArgs"] = None, ) -> Chat["SubmitInputArgs", ChatCompletion]: """ - Chat with an OpenAI model via the Completions API. + Chat with an OpenAI-compatible model (via the Completions API). This function exists mainly for historical reasons; new code should prefer `ChatOpenAI()`, which uses the newer Responses API. + + This function may also be useful for using a "OpenAI-compatible model" + hosted by another provider (e.g., vLLM, Ollama, etc.) that supports the + OpenAI Completions API. """ if isinstance(seed, MISSING_TYPE): seed = 1014 if is_testing() else None From 1bf13b65d17871df4410a11a46eb11a2647bb2da Mon Sep 17 00:00:00 2001 From: Carson Sievert Date: Tue, 11 Nov 2025 11:25:42 -0600 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- chatlas/_inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chatlas/_inspect.py b/chatlas/_inspect.py index 2ea40e0c..fbe20eda 100644 --- a/chatlas/_inspect.py +++ b/chatlas/_inspect.py @@ -217,11 +217,11 @@ def inspect_content_as_chatlas(content: str | InspectContent) -> Content: doc = content.document if content.mime_type == "application/pdf": url = None - data = b"" if doc.startswith("http://") or doc.startswith("https://"): url = doc + data = None else: - data = base64.b64decode(doc.replace("data:application/pdf;base64,", "")) + data = base64.b64decode(doc.split(",", 1)[1]) return ContentPDF(data=data, url=url, filename=content.filename) else: return ContentText(text=doc) From a2c78be7402cc77a4be5f612808c681b36e36b7a Mon Sep 17 00:00:00 2001 From: Carson Sievert Date: Tue, 11 Nov 2025 11:26:12 -0600 Subject: [PATCH 3/3] Update chatlas/_provider_openai_completions.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- chatlas/_provider_openai_completions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chatlas/_provider_openai_completions.py b/chatlas/_provider_openai_completions.py index 87d930ed..6f2d0374 100644 --- a/chatlas/_provider_openai_completions.py +++ b/chatlas/_provider_openai_completions.py @@ -69,7 +69,7 @@ def ChatOpenAICompletions( This function exists mainly for historical reasons; new code should prefer `ChatOpenAI()`, which uses the newer Responses API. - This function may also be useful for using a "OpenAI-compatible model" + This function may also be useful for using an "OpenAI-compatible model" hosted by another provider (e.g., vLLM, Ollama, etc.) that supports the OpenAI Completions API. """