llm.rb 12.2.0 → 12.3.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +167 -0
- data/README.md +51 -10
- data/data/openai.json +422 -0
- data/lib/llm/active_record/acts_as_agent.rb +11 -0
- data/lib/llm/agent.rb +14 -6
- data/lib/llm/compactor.rb +2 -2
- data/lib/llm/context.rb +16 -24
- data/lib/llm/object.rb +13 -0
- data/lib/llm/provider.rb +10 -7
- data/lib/llm/providers/anthropic.rb +2 -3
- data/lib/llm/providers/bedrock.rb +4 -5
- data/lib/llm/providers/google.rb +4 -2
- data/lib/llm/providers/mistral.rb +49 -0
- data/lib/llm/providers/ollama/response_adapter/completion.rb +39 -0
- data/lib/llm/providers/ollama.rb +2 -3
- data/lib/llm/providers/openai/responses.rb +6 -3
- data/lib/llm/providers/openai.rb +2 -3
- data/lib/llm/repl/bar.rb +52 -0
- data/lib/llm/repl/input.rb +143 -30
- data/lib/llm/repl/markdown.rb +85 -0
- data/lib/llm/repl/status.rb +16 -5
- data/lib/llm/repl/stream.rb +15 -5
- data/lib/llm/repl/transcript.rb +107 -18
- data/lib/llm/repl/window.rb +43 -17
- data/lib/llm/repl.rb +99 -18
- data/lib/llm/sequel/agent.rb +11 -0
- data/lib/llm/skill.rb +1 -1
- data/lib/llm/stream/disabled.rb +23 -0
- data/lib/llm/stream/io.rb +43 -0
- data/lib/llm/stream.rb +34 -0
- data/lib/llm/tools/git.rb +2 -3
- data/lib/llm/tools/pwd.rb +0 -1
- data/lib/llm/tools/rg.rb +2 -1
- data/lib/llm/tools/swap_text.rb +6 -0
- data/lib/llm/transport/execution.rb +1 -0
- data/lib/llm/version.rb +1 -1
- data/lib/llm.rb +10 -0
- data/llm.gemspec +7 -3
- data/resources/deepdive.md +170 -50
- metadata +10 -3
data/data/openai.json
CHANGED
|
@@ -124,6 +124,99 @@
|
|
|
124
124
|
"output": 168
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
|
+
"gpt-5.6": {
|
|
128
|
+
"id": "gpt-5.6",
|
|
129
|
+
"name": "GPT-5.6",
|
|
130
|
+
"description": "Frontier GPT-5.6 model for complex professional work, coding, and agentic workflows",
|
|
131
|
+
"family": "gpt",
|
|
132
|
+
"attachment": true,
|
|
133
|
+
"reasoning": true,
|
|
134
|
+
"reasoning_options": [
|
|
135
|
+
{
|
|
136
|
+
"type": "effort",
|
|
137
|
+
"values": [
|
|
138
|
+
"none",
|
|
139
|
+
"low",
|
|
140
|
+
"medium",
|
|
141
|
+
"high",
|
|
142
|
+
"xhigh",
|
|
143
|
+
"max"
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"tool_call": true,
|
|
148
|
+
"structured_output": true,
|
|
149
|
+
"temperature": false,
|
|
150
|
+
"knowledge": "2026-02-16",
|
|
151
|
+
"release_date": "2026-07-09",
|
|
152
|
+
"last_updated": "2026-07-09",
|
|
153
|
+
"modalities": {
|
|
154
|
+
"input": [
|
|
155
|
+
"text",
|
|
156
|
+
"image",
|
|
157
|
+
"pdf"
|
|
158
|
+
],
|
|
159
|
+
"output": [
|
|
160
|
+
"text"
|
|
161
|
+
]
|
|
162
|
+
},
|
|
163
|
+
"open_weights": false,
|
|
164
|
+
"limit": {
|
|
165
|
+
"context": 1050000,
|
|
166
|
+
"input": 922000,
|
|
167
|
+
"output": 128000
|
|
168
|
+
},
|
|
169
|
+
"experimental": {
|
|
170
|
+
"modes": {
|
|
171
|
+
"fast": {
|
|
172
|
+
"cost": {
|
|
173
|
+
"input": 10,
|
|
174
|
+
"output": 60,
|
|
175
|
+
"cache_read": 1,
|
|
176
|
+
"cache_write": 12.5
|
|
177
|
+
},
|
|
178
|
+
"provider": {
|
|
179
|
+
"body": {
|
|
180
|
+
"service_tier": "priority"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"pro": {
|
|
185
|
+
"provider": {
|
|
186
|
+
"body": {
|
|
187
|
+
"reasoning": {
|
|
188
|
+
"mode": "pro"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"cost": {
|
|
196
|
+
"input": 5,
|
|
197
|
+
"output": 30,
|
|
198
|
+
"cache_read": 0.5,
|
|
199
|
+
"cache_write": 6.25,
|
|
200
|
+
"tiers": [
|
|
201
|
+
{
|
|
202
|
+
"input": 10,
|
|
203
|
+
"output": 45,
|
|
204
|
+
"cache_read": 1,
|
|
205
|
+
"cache_write": 12.5,
|
|
206
|
+
"tier": {
|
|
207
|
+
"type": "context",
|
|
208
|
+
"size": 272000
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
],
|
|
212
|
+
"context_over_200k": {
|
|
213
|
+
"input": 10,
|
|
214
|
+
"output": 45,
|
|
215
|
+
"cache_read": 1,
|
|
216
|
+
"cache_write": 12.5
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
127
220
|
"gpt-5": {
|
|
128
221
|
"id": "gpt-5",
|
|
129
222
|
"name": "GPT-5",
|
|
@@ -930,6 +1023,99 @@
|
|
|
930
1023
|
"output": 0
|
|
931
1024
|
}
|
|
932
1025
|
},
|
|
1026
|
+
"gpt-5.6-luna": {
|
|
1027
|
+
"id": "gpt-5.6-luna",
|
|
1028
|
+
"name": "GPT-5.6 Luna",
|
|
1029
|
+
"description": "Cost-efficient GPT-5.6 model for fast, high-volume workloads",
|
|
1030
|
+
"family": "gpt-nano",
|
|
1031
|
+
"attachment": true,
|
|
1032
|
+
"reasoning": true,
|
|
1033
|
+
"reasoning_options": [
|
|
1034
|
+
{
|
|
1035
|
+
"type": "effort",
|
|
1036
|
+
"values": [
|
|
1037
|
+
"none",
|
|
1038
|
+
"low",
|
|
1039
|
+
"medium",
|
|
1040
|
+
"high",
|
|
1041
|
+
"xhigh",
|
|
1042
|
+
"max"
|
|
1043
|
+
]
|
|
1044
|
+
}
|
|
1045
|
+
],
|
|
1046
|
+
"tool_call": true,
|
|
1047
|
+
"structured_output": true,
|
|
1048
|
+
"temperature": false,
|
|
1049
|
+
"knowledge": "2026-02-16",
|
|
1050
|
+
"release_date": "2026-07-09",
|
|
1051
|
+
"last_updated": "2026-07-09",
|
|
1052
|
+
"modalities": {
|
|
1053
|
+
"input": [
|
|
1054
|
+
"text",
|
|
1055
|
+
"image",
|
|
1056
|
+
"pdf"
|
|
1057
|
+
],
|
|
1058
|
+
"output": [
|
|
1059
|
+
"text"
|
|
1060
|
+
]
|
|
1061
|
+
},
|
|
1062
|
+
"open_weights": false,
|
|
1063
|
+
"limit": {
|
|
1064
|
+
"context": 1050000,
|
|
1065
|
+
"input": 922000,
|
|
1066
|
+
"output": 128000
|
|
1067
|
+
},
|
|
1068
|
+
"experimental": {
|
|
1069
|
+
"modes": {
|
|
1070
|
+
"fast": {
|
|
1071
|
+
"cost": {
|
|
1072
|
+
"input": 2,
|
|
1073
|
+
"output": 12,
|
|
1074
|
+
"cache_read": 0.2,
|
|
1075
|
+
"cache_write": 2.5
|
|
1076
|
+
},
|
|
1077
|
+
"provider": {
|
|
1078
|
+
"body": {
|
|
1079
|
+
"service_tier": "priority"
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
"pro": {
|
|
1084
|
+
"provider": {
|
|
1085
|
+
"body": {
|
|
1086
|
+
"reasoning": {
|
|
1087
|
+
"mode": "pro"
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
},
|
|
1094
|
+
"cost": {
|
|
1095
|
+
"input": 1,
|
|
1096
|
+
"output": 6,
|
|
1097
|
+
"cache_read": 0.1,
|
|
1098
|
+
"cache_write": 1.25,
|
|
1099
|
+
"tiers": [
|
|
1100
|
+
{
|
|
1101
|
+
"input": 2,
|
|
1102
|
+
"output": 9,
|
|
1103
|
+
"cache_read": 0.2,
|
|
1104
|
+
"cache_write": 2.5,
|
|
1105
|
+
"tier": {
|
|
1106
|
+
"type": "context",
|
|
1107
|
+
"size": 272000
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
],
|
|
1111
|
+
"context_over_200k": {
|
|
1112
|
+
"input": 2,
|
|
1113
|
+
"output": 9,
|
|
1114
|
+
"cache_read": 0.2,
|
|
1115
|
+
"cache_write": 2.5
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
933
1119
|
"gpt-5.1-codex-mini": {
|
|
934
1120
|
"id": "gpt-5.1-codex-mini",
|
|
935
1121
|
"name": "GPT-5.1 Codex mini",
|
|
@@ -974,6 +1160,149 @@
|
|
|
974
1160
|
"cache_read": 0.025
|
|
975
1161
|
}
|
|
976
1162
|
},
|
|
1163
|
+
"gpt-realtime-2.1": {
|
|
1164
|
+
"id": "gpt-realtime-2.1",
|
|
1165
|
+
"name": "GPT-Realtime-2.1",
|
|
1166
|
+
"description": "Realtime speech-to-speech model with configurable reasoning, tool use, and robust voice-agent behavior",
|
|
1167
|
+
"family": "gpt",
|
|
1168
|
+
"attachment": true,
|
|
1169
|
+
"reasoning": true,
|
|
1170
|
+
"reasoning_options": [
|
|
1171
|
+
{
|
|
1172
|
+
"type": "effort",
|
|
1173
|
+
"values": [
|
|
1174
|
+
"minimal",
|
|
1175
|
+
"low",
|
|
1176
|
+
"medium",
|
|
1177
|
+
"high",
|
|
1178
|
+
"xhigh"
|
|
1179
|
+
]
|
|
1180
|
+
}
|
|
1181
|
+
],
|
|
1182
|
+
"tool_call": true,
|
|
1183
|
+
"structured_output": false,
|
|
1184
|
+
"temperature": false,
|
|
1185
|
+
"knowledge": "2024-09-30",
|
|
1186
|
+
"release_date": "2026-07-06",
|
|
1187
|
+
"last_updated": "2026-07-06",
|
|
1188
|
+
"modalities": {
|
|
1189
|
+
"input": [
|
|
1190
|
+
"text",
|
|
1191
|
+
"audio",
|
|
1192
|
+
"image"
|
|
1193
|
+
],
|
|
1194
|
+
"output": [
|
|
1195
|
+
"text",
|
|
1196
|
+
"audio"
|
|
1197
|
+
]
|
|
1198
|
+
},
|
|
1199
|
+
"open_weights": false,
|
|
1200
|
+
"limit": {
|
|
1201
|
+
"context": 128000,
|
|
1202
|
+
"input": 96000,
|
|
1203
|
+
"output": 32000
|
|
1204
|
+
},
|
|
1205
|
+
"cost": {
|
|
1206
|
+
"input": 4,
|
|
1207
|
+
"output": 24,
|
|
1208
|
+
"cache_read": 0.4,
|
|
1209
|
+
"input_audio": 32,
|
|
1210
|
+
"output_audio": 64
|
|
1211
|
+
}
|
|
1212
|
+
},
|
|
1213
|
+
"gpt-5.6-terra": {
|
|
1214
|
+
"id": "gpt-5.6-terra",
|
|
1215
|
+
"name": "GPT-5.6 Terra",
|
|
1216
|
+
"description": "Balanced GPT-5.6 model for capable, cost-efficient everyday work",
|
|
1217
|
+
"family": "gpt-mini",
|
|
1218
|
+
"attachment": true,
|
|
1219
|
+
"reasoning": true,
|
|
1220
|
+
"reasoning_options": [
|
|
1221
|
+
{
|
|
1222
|
+
"type": "effort",
|
|
1223
|
+
"values": [
|
|
1224
|
+
"none",
|
|
1225
|
+
"low",
|
|
1226
|
+
"medium",
|
|
1227
|
+
"high",
|
|
1228
|
+
"xhigh",
|
|
1229
|
+
"max"
|
|
1230
|
+
]
|
|
1231
|
+
}
|
|
1232
|
+
],
|
|
1233
|
+
"tool_call": true,
|
|
1234
|
+
"structured_output": true,
|
|
1235
|
+
"temperature": false,
|
|
1236
|
+
"knowledge": "2026-02-16",
|
|
1237
|
+
"release_date": "2026-07-09",
|
|
1238
|
+
"last_updated": "2026-07-09",
|
|
1239
|
+
"modalities": {
|
|
1240
|
+
"input": [
|
|
1241
|
+
"text",
|
|
1242
|
+
"image",
|
|
1243
|
+
"pdf"
|
|
1244
|
+
],
|
|
1245
|
+
"output": [
|
|
1246
|
+
"text"
|
|
1247
|
+
]
|
|
1248
|
+
},
|
|
1249
|
+
"open_weights": false,
|
|
1250
|
+
"limit": {
|
|
1251
|
+
"context": 1050000,
|
|
1252
|
+
"input": 922000,
|
|
1253
|
+
"output": 128000
|
|
1254
|
+
},
|
|
1255
|
+
"experimental": {
|
|
1256
|
+
"modes": {
|
|
1257
|
+
"fast": {
|
|
1258
|
+
"cost": {
|
|
1259
|
+
"input": 5,
|
|
1260
|
+
"output": 30,
|
|
1261
|
+
"cache_read": 0.5,
|
|
1262
|
+
"cache_write": 6.25
|
|
1263
|
+
},
|
|
1264
|
+
"provider": {
|
|
1265
|
+
"body": {
|
|
1266
|
+
"service_tier": "priority"
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
},
|
|
1270
|
+
"pro": {
|
|
1271
|
+
"provider": {
|
|
1272
|
+
"body": {
|
|
1273
|
+
"reasoning": {
|
|
1274
|
+
"mode": "pro"
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
},
|
|
1281
|
+
"cost": {
|
|
1282
|
+
"input": 2.5,
|
|
1283
|
+
"output": 15,
|
|
1284
|
+
"cache_read": 0.25,
|
|
1285
|
+
"cache_write": 3.125,
|
|
1286
|
+
"tiers": [
|
|
1287
|
+
{
|
|
1288
|
+
"input": 5,
|
|
1289
|
+
"output": 22.5,
|
|
1290
|
+
"cache_read": 0.5,
|
|
1291
|
+
"cache_write": 6.25,
|
|
1292
|
+
"tier": {
|
|
1293
|
+
"type": "context",
|
|
1294
|
+
"size": 272000
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
],
|
|
1298
|
+
"context_over_200k": {
|
|
1299
|
+
"input": 5,
|
|
1300
|
+
"output": 22.5,
|
|
1301
|
+
"cache_read": 0.5,
|
|
1302
|
+
"cache_write": 6.25
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
977
1306
|
"gpt-5.1-chat-latest": {
|
|
978
1307
|
"id": "gpt-5.1-chat-latest",
|
|
979
1308
|
"name": "GPT-5.1 Chat",
|
|
@@ -1851,6 +2180,99 @@
|
|
|
1851
2180
|
"cache_read": 0.075
|
|
1852
2181
|
}
|
|
1853
2182
|
},
|
|
2183
|
+
"gpt-5.6-sol": {
|
|
2184
|
+
"id": "gpt-5.6-sol",
|
|
2185
|
+
"name": "GPT-5.6 Sol",
|
|
2186
|
+
"description": "Frontier GPT-5.6 model for complex professional work, coding, and agentic workflows",
|
|
2187
|
+
"family": "gpt",
|
|
2188
|
+
"attachment": true,
|
|
2189
|
+
"reasoning": true,
|
|
2190
|
+
"reasoning_options": [
|
|
2191
|
+
{
|
|
2192
|
+
"type": "effort",
|
|
2193
|
+
"values": [
|
|
2194
|
+
"none",
|
|
2195
|
+
"low",
|
|
2196
|
+
"medium",
|
|
2197
|
+
"high",
|
|
2198
|
+
"xhigh",
|
|
2199
|
+
"max"
|
|
2200
|
+
]
|
|
2201
|
+
}
|
|
2202
|
+
],
|
|
2203
|
+
"tool_call": true,
|
|
2204
|
+
"structured_output": true,
|
|
2205
|
+
"temperature": false,
|
|
2206
|
+
"knowledge": "2026-02-16",
|
|
2207
|
+
"release_date": "2026-07-09",
|
|
2208
|
+
"last_updated": "2026-07-09",
|
|
2209
|
+
"modalities": {
|
|
2210
|
+
"input": [
|
|
2211
|
+
"text",
|
|
2212
|
+
"image",
|
|
2213
|
+
"pdf"
|
|
2214
|
+
],
|
|
2215
|
+
"output": [
|
|
2216
|
+
"text"
|
|
2217
|
+
]
|
|
2218
|
+
},
|
|
2219
|
+
"open_weights": false,
|
|
2220
|
+
"limit": {
|
|
2221
|
+
"context": 1050000,
|
|
2222
|
+
"input": 922000,
|
|
2223
|
+
"output": 128000
|
|
2224
|
+
},
|
|
2225
|
+
"experimental": {
|
|
2226
|
+
"modes": {
|
|
2227
|
+
"fast": {
|
|
2228
|
+
"cost": {
|
|
2229
|
+
"input": 10,
|
|
2230
|
+
"output": 60,
|
|
2231
|
+
"cache_read": 1,
|
|
2232
|
+
"cache_write": 12.5
|
|
2233
|
+
},
|
|
2234
|
+
"provider": {
|
|
2235
|
+
"body": {
|
|
2236
|
+
"service_tier": "priority"
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
"pro": {
|
|
2241
|
+
"provider": {
|
|
2242
|
+
"body": {
|
|
2243
|
+
"reasoning": {
|
|
2244
|
+
"mode": "pro"
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
},
|
|
2251
|
+
"cost": {
|
|
2252
|
+
"input": 5,
|
|
2253
|
+
"output": 30,
|
|
2254
|
+
"cache_read": 0.5,
|
|
2255
|
+
"cache_write": 6.25,
|
|
2256
|
+
"tiers": [
|
|
2257
|
+
{
|
|
2258
|
+
"input": 10,
|
|
2259
|
+
"output": 45,
|
|
2260
|
+
"cache_read": 1,
|
|
2261
|
+
"cache_write": 12.5,
|
|
2262
|
+
"tier": {
|
|
2263
|
+
"type": "context",
|
|
2264
|
+
"size": 272000
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
],
|
|
2268
|
+
"context_over_200k": {
|
|
2269
|
+
"input": 10,
|
|
2270
|
+
"output": 45,
|
|
2271
|
+
"cache_read": 1,
|
|
2272
|
+
"cache_write": 12.5
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
},
|
|
1854
2276
|
"gpt-5-codex": {
|
|
1855
2277
|
"id": "gpt-5-codex",
|
|
1856
2278
|
"name": "GPT-5-Codex",
|
|
@@ -75,6 +75,17 @@ module LLM::ActiveRecord
|
|
|
75
75
|
@llm
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
##
|
|
79
|
+
# @note
|
|
80
|
+
# This method does not persist to the database,
|
|
81
|
+
# but it can inspect and alter runtime state in
|
|
82
|
+
# a way that is temporary.
|
|
83
|
+
# @param (see LLM::Agent#repl)
|
|
84
|
+
# @return (see LLM::Agent#repl)
|
|
85
|
+
def repl(**params)
|
|
86
|
+
ctx.repl(**params)
|
|
87
|
+
end
|
|
88
|
+
|
|
78
89
|
private
|
|
79
90
|
|
|
80
91
|
##
|
data/lib/llm/agent.rb
CHANGED
|
@@ -398,17 +398,25 @@ module LLM
|
|
|
398
398
|
# By default this method disables the tracer for
|
|
399
399
|
# the duration of the repl session, and restores
|
|
400
400
|
# it afterwards.
|
|
401
|
-
# @param [Boolean]
|
|
401
|
+
# @param [Boolean] tracer
|
|
402
402
|
# When true, the tracer is kept alive during the
|
|
403
403
|
# repl session. Default is false.
|
|
404
|
+
# @param [Array<LLM::Tool>] tools
|
|
405
|
+
# Extra tools to attach for the repl session
|
|
406
|
+
# @param [Array<String>] skills
|
|
407
|
+
# Extra skills to attach for the repl session
|
|
404
408
|
# @return [void]
|
|
405
|
-
def repl(trace:
|
|
406
|
-
if
|
|
407
|
-
|
|
409
|
+
def repl(tracer: false, trace: nil, tools: [], skills: [])
|
|
410
|
+
if trace != nil
|
|
411
|
+
warn "llm.rb: trace option is deprecated, use tracer instead"
|
|
412
|
+
tracer = trace
|
|
413
|
+
end
|
|
414
|
+
if !tracer
|
|
415
|
+
previous = self.tracer
|
|
408
416
|
self.tracer = nil
|
|
409
417
|
end
|
|
410
418
|
require_relative "repl" unless defined?(::LLM::Repl)
|
|
411
|
-
LLM::Repl.new(self).start
|
|
419
|
+
LLM::Repl.new(agent: self, tools:, skills:).start
|
|
412
420
|
ensure
|
|
413
421
|
if !trace
|
|
414
422
|
self.tracer = previous
|
|
@@ -526,7 +534,7 @@ module LLM
|
|
|
526
534
|
max = params.key?(:tool_attempts) ? params.delete(:tool_attempts) : 25
|
|
527
535
|
max = Integer(max) if max
|
|
528
536
|
stream = params[:stream] || @ctx.params[:stream]
|
|
529
|
-
|
|
537
|
+
params[:stream] = LLM::Stream.try(stream, extra: {concurrency:})
|
|
530
538
|
res = talk.call(apply_instructions(prompt), params)
|
|
531
539
|
while @ctx.functions?
|
|
532
540
|
if max
|
data/lib/llm/compactor.rb
CHANGED
|
@@ -71,13 +71,13 @@ class LLM::Compactor
|
|
|
71
71
|
retention_window = [config[:retention_window], messages.size].min
|
|
72
72
|
return nil unless messages.size > retention_window
|
|
73
73
|
stream = ctx.params[:stream]
|
|
74
|
-
stream.on_compaction(ctx, self)
|
|
74
|
+
stream.on_compaction(ctx, self)
|
|
75
75
|
recent = retained_messages
|
|
76
76
|
older = messages[0...(messages.size - recent.size)]
|
|
77
77
|
summary = LLM::Message.new(ctx.llm.user_role, "[Previous conversation summary]\n\n#{summarize(older)}", {compaction: true})
|
|
78
78
|
ctx.messages.replace([*ctx.messages.take_while(&:system?), summary, *recent])
|
|
79
79
|
ctx.compacted = true
|
|
80
|
-
stream.on_compaction_finish(ctx, self)
|
|
80
|
+
stream.on_compaction_finish(ctx, self)
|
|
81
81
|
summary
|
|
82
82
|
end
|
|
83
83
|
|
data/lib/llm/context.rb
CHANGED
|
@@ -92,6 +92,8 @@ module LLM
|
|
|
92
92
|
@params[:tools] = tools unless tools.empty?
|
|
93
93
|
@params[:store] ||= false if @mode == :responses
|
|
94
94
|
@messages = LLM::Buffer.new(llm)
|
|
95
|
+
extra = @params.slice(:model, :tools).merge!(ctx: self, tracer:)
|
|
96
|
+
@params[:stream] = LLM::Stream.try(@params[:stream], extra:)
|
|
95
97
|
end
|
|
96
98
|
|
|
97
99
|
##
|
|
@@ -310,10 +312,7 @@ module LLM
|
|
|
310
312
|
# A list of functions to exclude from the wait
|
|
311
313
|
# @return [Array<LLM::Function::Return>]
|
|
312
314
|
def wait(strategy, except: [])
|
|
313
|
-
if
|
|
314
|
-
@queue = stream.queue
|
|
315
|
-
@queue.wait
|
|
316
|
-
else
|
|
315
|
+
if stream.queue.empty?
|
|
317
316
|
tools = except.empty? ? functions : functions - except
|
|
318
317
|
guards = guarded_returns(tools:)
|
|
319
318
|
return guards if guards
|
|
@@ -321,6 +320,9 @@ module LLM
|
|
|
321
320
|
returns = @queue.wait
|
|
322
321
|
emit_tool_returns(tools, returns)
|
|
323
322
|
returns
|
|
323
|
+
else
|
|
324
|
+
@queue = stream.queue
|
|
325
|
+
@queue.wait
|
|
324
326
|
end
|
|
325
327
|
ensure
|
|
326
328
|
@queue = nil
|
|
@@ -502,25 +504,11 @@ module LLM
|
|
|
502
504
|
|
|
503
505
|
private
|
|
504
506
|
|
|
505
|
-
##
|
|
506
|
-
# Binds runtime metadata onto an active stream.
|
|
507
|
-
# @api private
|
|
508
|
-
def bind!(stream, model, tools)
|
|
509
|
-
return unless LLM::Stream === stream
|
|
510
|
-
@stream = stream
|
|
511
|
-
stream.extra[:ctx] = self
|
|
512
|
-
stream.extra[:tracer] = tracer
|
|
513
|
-
stream.extra[:model] = model
|
|
514
|
-
stream.extra[:tools] = tools
|
|
515
|
-
end
|
|
516
|
-
|
|
517
507
|
##
|
|
518
508
|
# Returns the bound stream queue, if available.
|
|
519
509
|
# @api private
|
|
520
510
|
def queue
|
|
521
|
-
[@queue, stream
|
|
522
|
-
rescue NoMethodError
|
|
523
|
-
nil
|
|
511
|
+
[@queue, stream.queue].compact.first
|
|
524
512
|
end
|
|
525
513
|
|
|
526
514
|
##
|
|
@@ -543,12 +531,13 @@ module LLM
|
|
|
543
531
|
# Rewrites a prompt and params through the configured transformer.
|
|
544
532
|
# @api private
|
|
545
533
|
def transform(prompt, params)
|
|
534
|
+
transformer = self.transformer
|
|
546
535
|
return [prompt, params] unless transformer
|
|
547
536
|
stream = params[:stream]
|
|
548
|
-
stream.on_transform(self, transformer)
|
|
537
|
+
stream.on_transform(self, transformer)
|
|
549
538
|
transformer.call(self, prompt, params)
|
|
550
539
|
ensure
|
|
551
|
-
stream.on_transform_finish(self, transformer) if
|
|
540
|
+
stream.on_transform_finish(self, transformer) if transformer
|
|
552
541
|
end
|
|
553
542
|
|
|
554
543
|
##
|
|
@@ -556,8 +545,10 @@ module LLM
|
|
|
556
545
|
# @api private
|
|
557
546
|
def respond(prompt, params)
|
|
558
547
|
params = @params.merge(params)
|
|
548
|
+
extra = params.slice(:model, :tools).merge!(ctx: self, tracer:)
|
|
549
|
+
params[:stream] = LLM::Stream.try(params[:stream], extra:)
|
|
559
550
|
prompt, params = transform(prompt, params)
|
|
560
|
-
|
|
551
|
+
@stream = params[:stream]
|
|
561
552
|
res_id = params[:store] == false ? nil : @messages.find(&:assistant?)&.response&.response_id
|
|
562
553
|
input = res_id ? [] : @messages.to_a
|
|
563
554
|
params = params.merge(previous_response_id: res_id, input:).compact
|
|
@@ -570,8 +561,10 @@ module LLM
|
|
|
570
561
|
def complete(prompt, params)
|
|
571
562
|
params = params.merge(messages: @messages.to_a)
|
|
572
563
|
params = @params.merge(params)
|
|
564
|
+
extra = params.slice(:model, :tools).merge!(ctx: self, tracer:)
|
|
565
|
+
params[:stream] = LLM::Stream.try(params[:stream], extra:)
|
|
573
566
|
prompt, params = transform(prompt, params)
|
|
574
|
-
|
|
567
|
+
@stream = params[:stream]
|
|
575
568
|
[prompt, params, @llm.complete(prompt, params)]
|
|
576
569
|
end
|
|
577
570
|
|
|
@@ -590,7 +583,6 @@ module LLM
|
|
|
590
583
|
# Emits tool return callbacks for directly waited function work.
|
|
591
584
|
# @api private
|
|
592
585
|
def emit_tool_returns(tools, returns)
|
|
593
|
-
return unless LLM::Stream === stream
|
|
594
586
|
returns.each_with_index { |result, index| stream.on_tool_return(tools[index], result) }
|
|
595
587
|
end
|
|
596
588
|
|
data/lib/llm/object.rb
CHANGED
|
@@ -148,6 +148,19 @@ class LLM::Object < BasicObject
|
|
|
148
148
|
SINGLETON.from @h.merge(other)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
##
|
|
152
|
+
# @param [Hash, #to_h] other
|
|
153
|
+
# The hash to merge in place
|
|
154
|
+
# @return [LLM::Object]
|
|
155
|
+
# Returns self
|
|
156
|
+
def merge!(other = UNDEFINED)
|
|
157
|
+
return SINGLETON.get(@h, :merge!) if other.equal?(UNDEFINED)
|
|
158
|
+
other = ::Hash.try_convert(other)
|
|
159
|
+
raise TypeError, "#{other} cannot be coerced into a Hash" unless other
|
|
160
|
+
@h.merge!(other)
|
|
161
|
+
self
|
|
162
|
+
end
|
|
163
|
+
|
|
151
164
|
##
|
|
152
165
|
# @param [#to_s, #to_sym] k
|
|
153
166
|
# The key name
|
data/lib/llm/provider.rb
CHANGED
|
@@ -71,6 +71,16 @@ class LLM::Provider
|
|
|
71
71
|
raise NotImplementedError
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
##
|
|
75
|
+
# @note
|
|
76
|
+
# This feature is not implemented by all providers,
|
|
77
|
+
# and it will raise NotImplementedError for providers
|
|
78
|
+
# that do not support it.
|
|
79
|
+
# @return [LLM::Response]
|
|
80
|
+
def ocr(...)
|
|
81
|
+
raise NotImplementedError
|
|
82
|
+
end
|
|
83
|
+
|
|
74
84
|
##
|
|
75
85
|
# Provides an interface to the chat completions API
|
|
76
86
|
# @example
|
|
@@ -332,13 +342,6 @@ class LLM::Provider
|
|
|
332
342
|
transport.request_owner
|
|
333
343
|
end
|
|
334
344
|
|
|
335
|
-
##
|
|
336
|
-
# @param [Object] stream
|
|
337
|
-
# @return [Boolean]
|
|
338
|
-
def streamable?(stream)
|
|
339
|
-
LLM::Stream === stream || stream.respond_to?(:<<)
|
|
340
|
-
end
|
|
341
|
-
|
|
342
345
|
##
|
|
343
346
|
# @return [Boolean]
|
|
344
347
|
# Returns true when an API key is configured
|
|
@@ -151,9 +151,8 @@ module LLM
|
|
|
151
151
|
params = {role: :user, model: default_model, max_tokens: 1024}.merge!(params)
|
|
152
152
|
tools = resolve_tools(params.delete(:tools))
|
|
153
153
|
params = [params, adapt_tools(tools)].inject({}, &:merge!).compact
|
|
154
|
-
role, stream = params.delete(:role), params.delete(:stream)
|
|
155
|
-
params
|
|
156
|
-
[params, stream, tools, role]
|
|
154
|
+
role, stream = params.delete(:role), LLM::Stream.try(params.delete(:stream))
|
|
155
|
+
[params.merge!(stream: stream.enabled?), stream, tools, role]
|
|
157
156
|
end
|
|
158
157
|
|
|
159
158
|
def build_complete_request(prompt, params, role)
|