From a6967599705e124e40f1a252fb21ef70f96c82e9 Mon Sep 17 00:00:00 2001 From: Kerem Turgutlu Date: Thu, 4 Jun 2026 18:04:50 +0300 Subject: [PATCH] dict2obj --- fastllm/anthropic.py | 1 + fastllm/gemini.py | 1 + fastllm/openai_chat.py | 1 + fastllm/openai_responses.py | 1 + fastllm/types.py | 3 ++- nbs/00_types.ipynb | 29 ++++++++++++++++++++++++++++- nbs/02_oai_responses.ipynb | 18 ++---------------- nbs/03_oai_chat.ipynb | 1 + nbs/04_anthropic.ipynb | 1 + nbs/05_gemini.ipynb | 1 + 10 files changed, 39 insertions(+), 18 deletions(-) diff --git a/fastllm/anthropic.py b/fastllm/anthropic.py index 1ed485c..1f98ec3 100644 --- a/fastllm/anthropic.py +++ b/fastllm/anthropic.py @@ -85,6 +85,7 @@ def norm_parts(resp): # %% ../nbs/04_anthropic.ipynb #a3869e31 def norm_sse_event(ev, **kwargs): + ev = obj2dict(ev) typ = ev.get("type") text, thinking, tcs, citations = None, None, [], None if typ == "content_block_start": diff --git a/fastllm/gemini.py b/fastllm/gemini.py index ba89ef5..597c666 100644 --- a/fastllm/gemini.py +++ b/fastllm/gemini.py @@ -86,6 +86,7 @@ def norm_parts(resp): # %% ../nbs/05_gemini.ipynb #9a5024ee def norm_sse_event(ev, **kwargs): "Normalize Gemini stream event into Delta." + ev = obj2dict(ev) cand = nested_idx(ev, 'candidates', 0) or {} finish_reason = norm_finish(ev) parts = nested_idx(cand, 'content', 'parts') or [] diff --git a/fastllm/openai_chat.py b/fastllm/openai_chat.py index 05fe2cc..5c58b20 100644 --- a/fastllm/openai_chat.py +++ b/fastllm/openai_chat.py @@ -55,6 +55,7 @@ def norm_parts(resp): def norm_sse_event(ev, **kwargs): "Normalize a chat completion stream event." # usage always arrives as a single final event with choices: [] + ev = obj2dict(ev) fin = nested_idx(ev, 'choices', 0, 'finish_reason') tcs = norm_tool_calls(ev, delta=True) if (dlt:=nested_idx(ev, 'choices', 0, 'delta')) is not None: diff --git a/fastllm/openai_responses.py b/fastllm/openai_responses.py index 2a3bd35..ee503a1 100644 --- a/fastllm/openai_responses.py +++ b/fastllm/openai_responses.py @@ -82,6 +82,7 @@ def norm_parts(resp): # %% ../nbs/02_oai_responses.ipynb #7cd48aa5 def norm_sse_event(ev, **kwargs): "Normalize OpenAI Responses API stream event into Delta." + ev = obj2dict(ev) typ = ev.get("type") if typ == "response.output_text.delta": return Delta(text=ev.get("delta"), raw=ev, **kwargs) if typ == "response.reasoning_text.delta": return Delta(thinking=ev.get("delta",""), raw=ev, **kwargs) diff --git a/fastllm/types.py b/fastllm/types.py index b41fcb3..fe2c591 100644 --- a/fastllm/types.py +++ b/fastllm/types.py @@ -161,6 +161,7 @@ def register(self, name, finalize_usage=noop, **kwargs): self.apis[name] = Simpl # %% ../nbs/00_types.ipynb #d58a5f96 def mk_completion(resp, model, api_name, vendor_name): "Normalize an api response into Completion." + resp = obj2dict(resp) api = api_registry.apis[api_name] tcs = api.norm_tool_calls(resp) parts = api.norm_parts(resp) @@ -277,7 +278,7 @@ def get_model_meta(model, vendor_name=None, tfm=noop): if model in mp: key = model elif vendor_name=='gemini' and model.startswith('models/'): key = f"gemini/{model.removeprefix('models/')}" elif vendor_name: key = f"{vendor_name}/{model}" - return dict2obj(tfm(mp.get(key), model, vendor_name)) + return dict2obj(tfm(mp.get(key, {}), model, vendor_name)) # %% ../nbs/00_types.ipynb #60607e23 haik45 = "claude-haiku-4-5" diff --git a/nbs/00_types.ipynb b/nbs/00_types.ipynb index 69f3dd4..dfb9c6a 100644 --- a/nbs/00_types.ipynb +++ b/nbs/00_types.ipynb @@ -772,6 +772,7 @@ "#| export\n", "def mk_completion(resp, model, api_name, vendor_name):\n", " \"Normalize an api response into Completion.\"\n", + " resp = obj2dict(resp)\n", " api = api_registry.apis[api_name]\n", " tcs = api.norm_tool_calls(resp)\n", " parts = api.norm_parts(resp)\n", @@ -1015,7 +1016,7 @@ " if model in mp: key = model\n", " elif vendor_name=='gemini' and model.startswith('models/'): key = f\"gemini/{model.removeprefix('models/')}\"\n", " elif vendor_name: key = f\"{vendor_name}/{model}\"\n", - " return dict2obj(tfm(mp.get(key), model, vendor_name))" + " return dict2obj(tfm(mp.get(key, {}), model, vendor_name))" ] }, { @@ -1148,6 +1149,32 @@ " supports_vision=True, supports_image_input=True)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "68ae64b7", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "ERROR:root:No traceback has been produced, nothing to debug.\n" + ] + } + ], + "source": [ + "%debug" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dea5e0cd", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, diff --git a/nbs/02_oai_responses.ipynb b/nbs/02_oai_responses.ipynb index 1710749..8880384 100644 --- a/nbs/02_oai_responses.ipynb +++ b/nbs/02_oai_responses.ipynb @@ -69,22 +69,7 @@ "execution_count": null, "id": "1f198e96", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\u001b[92m16:32:22 - LiteLLM:WARNING\u001b[0m: common_utils.py:979 - litellm: could not pre-load bedrock-runtime response stream shape — Bedrock event-stream decoding will be unavailable. Error: No module named 'botocore'\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\u001b[92m16:32:23 - LiteLLM:WARNING\u001b[0m: common_utils.py:24 - litellm: could not pre-load sagemaker-runtime response stream shape — SageMaker event-stream decoding will be unavailable. Error: No module named 'botocore'\n" - ] - } - ], + "outputs": [], "source": [ "enable_cachy(hdrs=('content-type',))" ] @@ -791,6 +776,7 @@ "#| export\n", "def norm_sse_event(ev, **kwargs):\n", " \"Normalize OpenAI Responses API stream event into Delta.\"\n", + " ev = obj2dict(ev)\n", " typ = ev.get(\"type\")\n", " if typ == \"response.output_text.delta\": return Delta(text=ev.get(\"delta\"), raw=ev, **kwargs)\n", " if typ == \"response.reasoning_text.delta\": return Delta(thinking=ev.get(\"delta\",\"\"), raw=ev, **kwargs)\n", diff --git a/nbs/03_oai_chat.ipynb b/nbs/03_oai_chat.ipynb index eaf3c20..a020468 100644 --- a/nbs/03_oai_chat.ipynb +++ b/nbs/03_oai_chat.ipynb @@ -658,6 +658,7 @@ "def norm_sse_event(ev, **kwargs):\n", " \"Normalize a chat completion stream event.\"\n", " # usage always arrives as a single final event with choices: []\n", + " ev = obj2dict(ev)\n", " fin = nested_idx(ev, 'choices', 0, 'finish_reason')\n", " tcs = norm_tool_calls(ev, delta=True)\n", " if (dlt:=nested_idx(ev, 'choices', 0, 'delta')) is not None:\n", diff --git a/nbs/04_anthropic.ipynb b/nbs/04_anthropic.ipynb index c3e16b1..bbe4156 100644 --- a/nbs/04_anthropic.ipynb +++ b/nbs/04_anthropic.ipynb @@ -917,6 +917,7 @@ "source": [ "#| export\n", "def norm_sse_event(ev, **kwargs):\n", + " ev = obj2dict(ev)\n", " typ = ev.get(\"type\")\n", " text, thinking, tcs, citations = None, None, [], None\n", " if typ == \"content_block_start\":\n", diff --git a/nbs/05_gemini.ipynb b/nbs/05_gemini.ipynb index 1a1c579..992a348 100644 --- a/nbs/05_gemini.ipynb +++ b/nbs/05_gemini.ipynb @@ -1141,6 +1141,7 @@ "#| export\n", "def norm_sse_event(ev, **kwargs):\n", " \"Normalize Gemini stream event into Delta.\"\n", + " ev = obj2dict(ev)\n", " cand = nested_idx(ev, 'candidates', 0) or {}\n", " finish_reason = norm_finish(ev)\n", " parts = nested_idx(cand, 'content', 'parts') or []\n",