1313# ---- helpers --------------------------------------------------------------
1414
1515class _FakeStream :
16- def __init__ (self , lines : Iterable [str ]):
16+ def __init__ (self , lines : Iterable [str ], status_code : int = 200 , body : bytes = b"" ):
1717 self ._lines = list (lines )
18+ self .status_code = status_code
19+ self ._body = body
1820
1921 async def aiter_lines (self ):
2022 for line in self ._lines :
2123 yield line
2224
25+ async def aread (self ):
26+ return self ._body
27+
2328 async def __aenter__ (self ):
2429 return self
2530
@@ -28,13 +33,15 @@ async def __aexit__(self, *exc):
2833
2934
3035class _FakeAsyncClient :
31- def __init__ (self , lines : Iterable [str ]):
36+ def __init__ (self , lines : Iterable [str ], status_code : int = 200 , body : bytes = b"" ):
3237 self ._lines = list (lines )
38+ self ._status_code = status_code
39+ self ._body = body
3340 self .last_call : dict | None = None
3441
3542 def stream (self , method , url , ** kw ):
3643 self .last_call = {"method" : method , "url" : url , ** kw }
37- return _FakeStream (self ._lines )
44+ return _FakeStream (self ._lines , status_code = self . _status_code , body = self . _body )
3845
3946 async def aclose (self ):
4047 pass
@@ -112,3 +119,20 @@ async def test_stream_skips_empty_and_malformed_lines():
112119 out = [c async for c in adapter .stream ([{"role" : "user" , "content" : "hi" }])]
113120 assert [c .delta for c in out ] == ["ok" ]
114121 assert out [- 1 ].done is True
122+
123+
124+ @pytest .mark .asyncio
125+ async def test_stream_raises_on_upstream_error ():
126+ """A non-2xx response (e.g. 404 unknown model) returns a JSON error body,
127+ not chat events. Without a status guard the loop skips it and yields an
128+ empty completion silently; the adapter must raise with status + detail."""
129+ body = json .dumps ({"error" : "model 'ghost' not found" }).encode ("utf-8" )
130+ fake = _FakeAsyncClient ([body .decode ()], status_code = 404 , body = body )
131+ adapter = OllamaAdapter (base_url = "http://x" , model = "ghost" , http = fake )
132+
133+ with pytest .raises (RuntimeError ) as ei :
134+ async for _ in adapter .stream ([{"role" : "user" , "content" : "hi" }]):
135+ pass
136+ msg = str (ei .value )
137+ assert "404" in msg
138+ assert "model 'ghost' not found" in msg
0 commit comments