langchain-llama-cpp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langchain_llama_cpp/__init__.py +27 -0
- langchain_llama_cpp/_wire.py +328 -0
- langchain_llama_cpp/chat_models.py +1350 -0
- langchain_llama_cpp/content_blocks.py +73 -0
- langchain_llama_cpp/exceptions.py +197 -0
- langchain_llama_cpp/py.typed +0 -0
- langchain_llama_cpp/utils.py +34 -0
- langchain_llama_cpp-0.1.0.dist-info/METADATA +248 -0
- langchain_llama_cpp-0.1.0.dist-info/RECORD +11 -0
- langchain_llama_cpp-0.1.0.dist-info/WHEEL +4 -0
- langchain_llama_cpp-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""LangChain chat model for llama.cpp's llama-server."""
|
|
2
|
+
|
|
3
|
+
from langchain_llama_cpp.chat_models import ChatLlama
|
|
4
|
+
from langchain_llama_cpp.exceptions import (
|
|
5
|
+
LlamaAPIError,
|
|
6
|
+
LlamaAuthenticationError,
|
|
7
|
+
LlamaConnectionError,
|
|
8
|
+
LlamaContextOverflowError,
|
|
9
|
+
LlamaInvalidRequestError,
|
|
10
|
+
LlamaModelNotFoundError,
|
|
11
|
+
LlamaPermissionDeniedError,
|
|
12
|
+
LlamaRateLimitError,
|
|
13
|
+
LlamaTimeoutError,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ChatLlama",
|
|
18
|
+
"LlamaAPIError",
|
|
19
|
+
"LlamaAuthenticationError",
|
|
20
|
+
"LlamaConnectionError",
|
|
21
|
+
"LlamaContextOverflowError",
|
|
22
|
+
"LlamaInvalidRequestError",
|
|
23
|
+
"LlamaModelNotFoundError",
|
|
24
|
+
"LlamaPermissionDeniedError",
|
|
25
|
+
"LlamaRateLimitError",
|
|
26
|
+
"LlamaTimeoutError",
|
|
27
|
+
]
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""The JSON llama-server sends back from `/v1/chat/completions` (b11429).
|
|
2
|
+
|
|
3
|
+
Types for the type checker only: replies are plain dicts from `json.loads`,
|
|
4
|
+
never validated, so a server that sends something else isn't rejected here.
|
|
5
|
+
Every class is `total=False` because the code reads each key with `.get()`
|
|
6
|
+
and copes when it's missing; the docstrings say which keys the server always
|
|
7
|
+
sends.
|
|
8
|
+
|
|
9
|
+
Sources (llama.cpp b11429): `tools/server/server-task.cpp`
|
|
10
|
+
(`to_json_oaicompat_chat`, `to_json_oaicompat_chat_stream`,
|
|
11
|
+
`server_task_result_cmpl_partial::to_json_oaicompat_chat`,
|
|
12
|
+
`probs_vector_to_json`, `result_prompt_progress::to_json`),
|
|
13
|
+
`server-chat.cpp` (`server_chat_msg_diff_to_json_oaicompat`),
|
|
14
|
+
`server-common.cpp` (`server_slot_stats::to_json`, `format_error_response`),
|
|
15
|
+
`common/chat.cpp` (`common_chat_msg::to_json_oaicompat`).
|
|
16
|
+
|
|
17
|
+
A reply (`stream` off):
|
|
18
|
+
|
|
19
|
+
{"id": "chatcmpl-...", "object": "chat.completion", "created": 1760000000,
|
|
20
|
+
"model": "google/gemma-4-26B-A4B-it-qat-q4_0-gguf:Q4_0",
|
|
21
|
+
"system_fingerprint": "b11429-d81235049",
|
|
22
|
+
"choices": [{
|
|
23
|
+
"index": 0, "finish_reason": "tool_calls",
|
|
24
|
+
"message": {
|
|
25
|
+
"role": "assistant", "content": "", "reasoning_content": "...",
|
|
26
|
+
"tool_calls": [{
|
|
27
|
+
"type": "function", "id": "...",
|
|
28
|
+
"function": {"name": "get_weather", "arguments": "<JSON text>"}}]}}],
|
|
29
|
+
"usage": {"prompt_tokens": 32, "completion_tokens": 5, "total_tokens": 37,
|
|
30
|
+
"prompt_tokens_details": {"cached_tokens": 27}},
|
|
31
|
+
"timings": {"cache_n": 27, "prompt_n": 5, ...}}
|
|
32
|
+
|
|
33
|
+
A stream (`stream` on) is SSE `data:` lines, each a `LlamaChunk`, then
|
|
34
|
+
`data: [DONE]`:
|
|
35
|
+
|
|
36
|
+
{"choices": [{"index": 0, "finish_reason": null,
|
|
37
|
+
"delta": {"role": "assistant", "content": null}}], ...} # first
|
|
38
|
+
{"choices": [{"index": 0, "finish_reason": null,
|
|
39
|
+
"delta": {"reasoning_content": "Let"}}], ...}
|
|
40
|
+
{"choices": [{"index": 0, "finish_reason": null,
|
|
41
|
+
"delta": {"tool_calls": [{
|
|
42
|
+
"index": 0, "id": "...", "type": "function",
|
|
43
|
+
"function": {"name": "get_weather"}}]}}], ...}
|
|
44
|
+
{"choices": [{"index": 0, "finish_reason": null,
|
|
45
|
+
"delta": {"tool_calls": [{
|
|
46
|
+
"index": 0, "function": {"arguments": "<JSON piece>"}}]}}],
|
|
47
|
+
...}
|
|
48
|
+
{"choices": [{"index": 0, "finish_reason": "tool_calls", "delta": {}}], ...}
|
|
49
|
+
{"choices": [], "usage": {...}, "timings": {...}, ...} # with include_usage
|
|
50
|
+
|
|
51
|
+
A failure after the first chunk arrives as `data: {"error": LlamaError}` and
|
|
52
|
+
the stream ends without `[DONE]`.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
from typing import Literal, TypedDict
|
|
56
|
+
|
|
57
|
+
FinishReason = Literal["stop", "length", "tool_calls"]
|
|
58
|
+
"""`length` when `max_tokens` or the context window ran out (b11429 sends
|
|
59
|
+
`length` for every stop that isn't an end-of-sequence token or a stop string)."""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LlamaFunction(TypedDict, total=False):
|
|
63
|
+
"""A tool call's function. In a reply both keys are sent.
|
|
64
|
+
|
|
65
|
+
In a stream, `name` comes in the first fragment and `arguments` in pieces
|
|
66
|
+
after it; a fragment leaves out whichever it doesn't carry.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
name: str
|
|
70
|
+
|
|
71
|
+
arguments: str
|
|
72
|
+
"""JSON text, not an object: `'{"city": "Paris"}'`."""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LlamaToolCall(TypedDict, total=False):
|
|
76
|
+
"""One tool call.
|
|
77
|
+
|
|
78
|
+
In a reply: `type`, `function`, and `id` (left out if the template made
|
|
79
|
+
none). In a stream fragment: always `index`; `id` and `type` only in the
|
|
80
|
+
first fragment of each call; `function` when there's a name or arguments.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
index: int
|
|
84
|
+
"""Which call a stream fragment belongs to (0, 1, ...). Stream only."""
|
|
85
|
+
|
|
86
|
+
id: str
|
|
87
|
+
|
|
88
|
+
type: Literal["function"]
|
|
89
|
+
|
|
90
|
+
function: LlamaFunction
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class LlamaContentPart(TypedDict, total=False):
|
|
94
|
+
"""A content part.
|
|
95
|
+
|
|
96
|
+
`common_chat_msg::to_json_oaicompat` writes `content` as a list of these
|
|
97
|
+
when the message holds parts instead of text; the reply parsers write
|
|
98
|
+
text, so a list hasn't been seen in a reply.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
type: str
|
|
102
|
+
text: str
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class LlamaMessage(TypedDict, total=False):
|
|
106
|
+
"""`choices[].message` in a reply. Always `role` and `content`."""
|
|
107
|
+
|
|
108
|
+
role: Literal["assistant"]
|
|
109
|
+
|
|
110
|
+
content: str | list[LlamaContentPart]
|
|
111
|
+
"""`""` when the model only called tools."""
|
|
112
|
+
|
|
113
|
+
reasoning_content: str
|
|
114
|
+
"""The thinking, separate from `content`. Left out when there was none."""
|
|
115
|
+
|
|
116
|
+
tool_calls: list[LlamaToolCall]
|
|
117
|
+
"""Left out when there were no calls."""
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class LlamaDelta(TypedDict, total=False):
|
|
121
|
+
"""`choices[].delta` in a stream chunk. Each key only when it changed.
|
|
122
|
+
|
|
123
|
+
The first chunk is `{"role": "assistant", "content": None}`; the finish
|
|
124
|
+
chunk is `{}`. `tool_calls` holds one fragment at a time.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
role: Literal["assistant"]
|
|
128
|
+
content: str | None
|
|
129
|
+
reasoning_content: str
|
|
130
|
+
tool_calls: list[LlamaToolCall]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class LlamaTopLogprob(TypedDict, total=False):
|
|
134
|
+
"""One candidate token. `logprob`, or `prob` with `post_sampling_probs`."""
|
|
135
|
+
|
|
136
|
+
id: int
|
|
137
|
+
token: str
|
|
138
|
+
bytes: list[int]
|
|
139
|
+
logprob: float
|
|
140
|
+
prob: float
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class LlamaTokenLogprob(LlamaTopLogprob, total=False):
|
|
144
|
+
"""One generated token and its candidates.
|
|
145
|
+
|
|
146
|
+
`top_logprobs`, or `top_probs` with `post_sampling_probs`.
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
top_logprobs: list[LlamaTopLogprob]
|
|
150
|
+
top_probs: list[LlamaTopLogprob]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class LlamaLogprobs(TypedDict, total=False):
|
|
154
|
+
"""`choices[].logprobs`, only when `logprobs` or `n_probs` asked for it.
|
|
155
|
+
|
|
156
|
+
In a stream, each chunk carries the tokens it generated.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
content: list[LlamaTokenLogprob]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class LlamaChoice(TypedDict, total=False):
|
|
163
|
+
"""`choices[]` in a reply. Always `index`, `finish_reason` and `message`."""
|
|
164
|
+
|
|
165
|
+
index: int
|
|
166
|
+
finish_reason: FinishReason
|
|
167
|
+
message: LlamaMessage
|
|
168
|
+
logprobs: LlamaLogprobs
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class LlamaChunkChoice(TypedDict, total=False):
|
|
172
|
+
"""`choices[]` in a stream chunk. Always `index`, `finish_reason`, `delta`.
|
|
173
|
+
|
|
174
|
+
`finish_reason` is None until the finish chunk.
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
index: int
|
|
178
|
+
finish_reason: FinishReason | None
|
|
179
|
+
delta: LlamaDelta
|
|
180
|
+
logprobs: LlamaLogprobs
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class LlamaPromptTokensDetails(TypedDict, total=False):
|
|
184
|
+
"""`usage.prompt_tokens_details`."""
|
|
185
|
+
|
|
186
|
+
cached_tokens: int
|
|
187
|
+
"""Prompt tokens taken from the KV cache. Part of `prompt_tokens`."""
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class LlamaCompletionTokensDetails(TypedDict, total=False):
|
|
191
|
+
"""`usage.completion_tokens_details`, as OpenAI sends it.
|
|
192
|
+
|
|
193
|
+
b11429 never sends it (reasoning tokens are counted in
|
|
194
|
+
`completion_tokens` but not reported apart); read in case a build does.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
reasoning_tokens: int
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class LlamaUsage(TypedDict, total=False):
|
|
201
|
+
"""`usage`. b11429 always sends the four keys below except the last."""
|
|
202
|
+
|
|
203
|
+
prompt_tokens: int
|
|
204
|
+
|
|
205
|
+
completion_tokens: int
|
|
206
|
+
"""Includes thinking tokens."""
|
|
207
|
+
|
|
208
|
+
total_tokens: int
|
|
209
|
+
|
|
210
|
+
prompt_tokens_details: LlamaPromptTokensDetails
|
|
211
|
+
|
|
212
|
+
completion_tokens_details: LlamaCompletionTokensDetails
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class LlamaTimings(TypedDict, total=False):
|
|
216
|
+
"""`timings`, llama-server's own.
|
|
217
|
+
|
|
218
|
+
The `draft_*` keys come only with speculative decoding.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
cache_n: int
|
|
222
|
+
"""Prompt tokens reused from the KV cache."""
|
|
223
|
+
|
|
224
|
+
prompt_n: int
|
|
225
|
+
"""Prompt tokens processed for this request."""
|
|
226
|
+
|
|
227
|
+
prompt_ms: float
|
|
228
|
+
|
|
229
|
+
prompt_per_token_ms: float
|
|
230
|
+
|
|
231
|
+
prompt_per_second: float
|
|
232
|
+
|
|
233
|
+
predicted_n: int
|
|
234
|
+
"""Tokens generated, thinking included."""
|
|
235
|
+
|
|
236
|
+
predicted_ms: float
|
|
237
|
+
|
|
238
|
+
predicted_per_token_ms: float
|
|
239
|
+
|
|
240
|
+
predicted_per_second: float
|
|
241
|
+
|
|
242
|
+
draft_n: int
|
|
243
|
+
|
|
244
|
+
draft_n_accepted: int
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class LlamaPromptProgress(TypedDict, total=False):
|
|
248
|
+
"""`prompt_progress`, in stream chunks with `return_progress`."""
|
|
249
|
+
|
|
250
|
+
total: int
|
|
251
|
+
cache: int
|
|
252
|
+
processed: int
|
|
253
|
+
time_ms: int
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class LlamaResponse(TypedDict, total=False):
|
|
257
|
+
"""A `chat.completion` reply. Always every key but `timings`."""
|
|
258
|
+
|
|
259
|
+
id: str
|
|
260
|
+
"""`chatcmpl-...`."""
|
|
261
|
+
|
|
262
|
+
object: Literal["chat.completion"]
|
|
263
|
+
|
|
264
|
+
created: int
|
|
265
|
+
"""Unix time, in seconds."""
|
|
266
|
+
|
|
267
|
+
model: str
|
|
268
|
+
"""The file llama-swap loaded, not the entry name that was requested."""
|
|
269
|
+
|
|
270
|
+
system_fingerprint: str
|
|
271
|
+
"""The server build, e.g. `b11429-d81235049`."""
|
|
272
|
+
|
|
273
|
+
choices: list[LlamaChoice]
|
|
274
|
+
"""One per completion: `n` of them."""
|
|
275
|
+
|
|
276
|
+
usage: LlamaUsage
|
|
277
|
+
|
|
278
|
+
timings: LlamaTimings
|
|
279
|
+
"""Sent when the slot recorded timings (in every reply seen so far)."""
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class LlamaChunk(TypedDict, total=False):
|
|
283
|
+
"""A `chat.completion.chunk` stream event.
|
|
284
|
+
|
|
285
|
+
Always `id`, `object`, `created`, `model`, `system_fingerprint` and
|
|
286
|
+
`choices`. `choices` is empty only in the usage chunk at the end (with
|
|
287
|
+
`stream_options.include_usage`), which carries `usage`; `timings` goes on
|
|
288
|
+
the last chunk sent, and on others with `timings_per_token`.
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
id: str
|
|
292
|
+
object: Literal["chat.completion.chunk"]
|
|
293
|
+
created: int
|
|
294
|
+
model: str
|
|
295
|
+
system_fingerprint: str
|
|
296
|
+
choices: list[LlamaChunkChoice]
|
|
297
|
+
usage: LlamaUsage
|
|
298
|
+
timings: LlamaTimings
|
|
299
|
+
prompt_progress: LlamaPromptProgress
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class LlamaError(TypedDict, total=False):
|
|
303
|
+
"""The `error` object of an HTTP error reply or a stream `error` event.
|
|
304
|
+
|
|
305
|
+
Always `code` (the HTTP status), `message` and `type`. llama-swap's own
|
|
306
|
+
errors (e.g. 404 for an unknown model) are plain text, not this.
|
|
307
|
+
"""
|
|
308
|
+
|
|
309
|
+
code: int
|
|
310
|
+
|
|
311
|
+
message: str
|
|
312
|
+
|
|
313
|
+
type: Literal[
|
|
314
|
+
"invalid_request_error",
|
|
315
|
+
"authentication_error",
|
|
316
|
+
"not_found_error",
|
|
317
|
+
"server_error",
|
|
318
|
+
"permission_error",
|
|
319
|
+
"not_supported_error",
|
|
320
|
+
"unavailable_error",
|
|
321
|
+
"exceed_context_size_error",
|
|
322
|
+
]
|
|
323
|
+
|
|
324
|
+
n_prompt_tokens: int
|
|
325
|
+
"""With `exceed_context_size_error`: tokens in the rejected prompt."""
|
|
326
|
+
|
|
327
|
+
n_ctx: int
|
|
328
|
+
"""With `exceed_context_size_error`: the context window."""
|