@lampdotdev/lamp 0.36.2-nightly.28 → 0.36.2-nightly.29

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.
package/cli/ai.mjs CHANGED
@@ -212,6 +212,14 @@ export function models() {
212
212
  return { models: listedHere, default: defaultId(which) };
213
213
  }
214
214
 
215
+ /**
216
+ * How long a page's request may take: a real answer is never cut off however long the model takes, and the guard only
217
+ * ends a call that has stopped answering (`MAX_TURN_SECONDS` for a step of `act`, the same here). A page that wants a
218
+ * quicker answer asks for one (`seconds`), never a longer one.
219
+ */
220
+ const GUARD_SECONDS = 600;
221
+ const secondsFor = (body) => (typeof body?.seconds === "number" && body.seconds > 0 ? Math.min(body.seconds, GUARD_SECONDS) : GUARD_SECONDS);
222
+
215
223
  /** `/ai`: a text answer, or JSON when asked for it. */
216
224
  export async function ask(body) {
217
225
  const prompt = typeof body?.prompt === "string" ? body.prompt.trim() : "";
@@ -219,7 +227,7 @@ export async function ask(body) {
219
227
  let system = typeof body.system === "string" ? body.system : "";
220
228
  if (body.json) system += (system ? "\n\n" : "") + "Answer with JSON only, nothing around it.";
221
229
  const which = engine();
222
- try { return [200, { text: await complete({ system, prompt, images: body.images, which, model: modelFor(which, body.model) }) }]; }
230
+ try { return [200, { text: await complete({ system, prompt, images: body.images, which, model: modelFor(which, body.model), seconds: secondsFor(body) }) }]; }
223
231
  catch (error) { return [502, { error: error.message }]; }
224
232
  }
225
233
 
@@ -233,7 +241,7 @@ export async function decide(body) {
233
241
  }
234
242
  const { system, prompt, schema } = decidePrompt(body.state, questions);
235
243
  let text;
236
- try { text = await complete({ system, prompt, schema }); } catch (error) { return [502, { error: error.message }]; }
244
+ try { text = await complete({ system, prompt, schema, seconds: secondsFor(body) }); } catch (error) { return [502, { error: error.message }]; }
237
245
  const read = readDecision(text, questions);
238
246
  return read.answers ? [200, { answers: read.answers }] : [502, { error: read.problem }];
239
247
  }
package/cli/calls.mjs CHANGED
@@ -373,7 +373,7 @@ export const CALLS = {
373
373
  */
374
374
  async openRouterText(given, { stopped } = {}) {
375
375
  return openRouter(() => openRouterText({ key: text(given, "key"), prompt: text(given, "prompt"), images: given.images, system: given.system ?? "",
376
- model: text(given, "model"), json: given.json === true, schema: given.schema ?? undefined, seconds: seconds(given.seconds, 120, 600), signal: stopped }));
376
+ model: text(given, "model"), json: given.json === true, schema: given.schema ?? undefined, seconds: seconds(given.seconds, 600, 600), signal: stopped }));
377
377
  },
378
378
 
379
379
  /** One chat completion from OpenRouter, its request as `body` (`actAsk`'s): `{message, cost}`, or `{trouble}`. */
@@ -384,7 +384,7 @@ export const CALLS = {
384
384
  /** Decisions from OpenRouter's decision model (`AppAI.decide`): `key`, `model`, `state`, `questions`. `{answers, cost}` or `{trouble}`. */
385
385
  async openRouterDecide(given, { stopped } = {}) {
386
386
  return openRouter(() => openRouterDecide({ key: text(given, "key"), model: text(given, "model"), state: given.state, questions: given.questions ?? {},
387
- seconds: seconds(given.seconds, 60, 120), signal: stopped }));
387
+ seconds: seconds(given.seconds, 600, 600), signal: stopped }));
388
388
  },
389
389
 
390
390
  /** A picture from OpenRouter (`AppAI.image`): `key`, `model`, `prompt`, `references`, `aspectRatio`. `{image, cost}` or `{trouble}`. */
@@ -403,7 +403,7 @@ export const CALLS = {
403
403
  return { text: await complete({ which: text(given, "engine"), tool: typeof given.tool === "string" && given.tool ? given.tool : undefined,
404
404
  env: given.env && typeof given.env === "object" ? given.env : {}, system: typeof given.system === "string" ? given.system : "",
405
405
  prompt: text(given, "prompt"), schema: given.schema ?? undefined, model: typeof given.model === "string" ? given.model : "",
406
- images: Array.isArray(given.images) ? given.images : [], seconds: seconds(given.seconds, 300, 600), signal: stopped }) };
406
+ images: Array.isArray(given.images) ? given.images : [], seconds: seconds(given.seconds, 600, 600), signal: stopped }) };
407
407
  } catch (error) {
408
408
  if (error instanceof EngineTrouble) return { trouble: { kind: error.kind, message: error.message } };
409
409
  throw error;
@@ -301,6 +301,10 @@
301
301
  });
302
302
  }
303
303
 
304
+ // How long the page waits for an answer: as long as Lamp's guard on the call (10 minutes), or the page's own limit,
305
+ // with a little more for the answer to come back.
306
+ function AI_WAIT(seconds) { return (typeof seconds === 'number' && seconds > 0 ? Math.min(seconds, 600) : 600) * 1000 + 15000; }
307
+
304
308
  function post(path, body, timeout, deadline) {
305
309
  timeout = timeout || 240000;
306
310
  var done;
@@ -485,7 +489,9 @@
485
489
  return encode(o.images).then(function (images) {
486
490
  var body = { prompt: text, system: rules.filter(function (r) { return r; }).join('\n\n'), json: !!o.json, images: images };
487
491
  if (o.model) body.model = o.model;
488
- return post('/ai', body);
492
+ // No limit of the page's own unless it asks for one: Lamp's guard ends only a call that stopped answering.
493
+ if (typeof o.seconds === 'number' && o.seconds > 0) body.seconds = o.seconds;
494
+ return post('/ai', body, AI_WAIT(body.seconds));
489
495
  }).then(function (reply) {
490
496
  if (typeof reply.text !== 'string') throw new Failure('The AI request failed.');
491
497
  if (!o.json) return reply.text;
@@ -998,7 +1004,7 @@
998
1004
  },
999
1005
  /** Asks several questions about the same thing in one request. Answers come back under the same keys. */
1000
1006
  decide: function (about, questions) {
1001
- return post('/ai/decide', { state: decisionState(about), questions: questions }).then(function (reply) {
1007
+ return post('/ai/decide', { state: decisionState(about), questions: questions }, AI_WAIT()).then(function (reply) {
1002
1008
  var answers = {};
1003
1009
  for (var key in (reply.answers || {})) if (reply.answers.hasOwnProperty(key)) answers[key] = decision(reply.answers[key]);
1004
1010
  return answers;
@@ -1036,7 +1042,7 @@
1036
1042
  finish(i, answers);
1037
1043
  return Promise.resolve();
1038
1044
  }
1039
- return post('/ai/decide', { state: decisionState(state), questions: questions }).then(function (reply) {
1045
+ return post('/ai/decide', { state: decisionState(state), questions: questions }, AI_WAIT()).then(function (reply) {
1040
1046
  var answers = {};
1041
1047
  for (var k in (reply.answers || {})) if (reply.answers.hasOwnProperty(k)) answers[k] = decision(reply.answers[k]);
1042
1048
  if (key) saved[key] = { answers: reply.answers, until: now + o.remember * 1000 };
package/cli/lamps/lamp.js CHANGED
@@ -301,6 +301,10 @@
301
301
  });
302
302
  }
303
303
 
304
+ // How long the page waits for an answer: as long as Lamp's guard on the call (10 minutes), or the page's own limit,
305
+ // with a little more for the answer to come back.
306
+ function AI_WAIT(seconds) { return (typeof seconds === 'number' && seconds > 0 ? Math.min(seconds, 600) : 600) * 1000 + 15000; }
307
+
304
308
  function post(path, body, timeout, deadline) {
305
309
  timeout = timeout || 240000;
306
310
  var done;
@@ -485,7 +489,9 @@
485
489
  return encode(o.images).then(function (images) {
486
490
  var body = { prompt: text, system: rules.filter(function (r) { return r; }).join('\n\n'), json: !!o.json, images: images };
487
491
  if (o.model) body.model = o.model;
488
- return post('/ai', body);
492
+ // No limit of the page's own unless it asks for one: Lamp's guard ends only a call that stopped answering.
493
+ if (typeof o.seconds === 'number' && o.seconds > 0) body.seconds = o.seconds;
494
+ return post('/ai', body, AI_WAIT(body.seconds));
489
495
  }).then(function (reply) {
490
496
  if (typeof reply.text !== 'string') throw new Failure('The AI request failed.');
491
497
  if (!o.json) return reply.text;
@@ -998,7 +1004,7 @@
998
1004
  },
999
1005
  /** Asks several questions about the same thing in one request. Answers come back under the same keys. */
1000
1006
  decide: function (about, questions) {
1001
- return post('/ai/decide', { state: decisionState(about), questions: questions }).then(function (reply) {
1007
+ return post('/ai/decide', { state: decisionState(about), questions: questions }, AI_WAIT()).then(function (reply) {
1002
1008
  var answers = {};
1003
1009
  for (var key in (reply.answers || {})) if (reply.answers.hasOwnProperty(key)) answers[key] = decision(reply.answers[key]);
1004
1010
  return answers;
@@ -1036,7 +1042,7 @@
1036
1042
  finish(i, answers);
1037
1043
  return Promise.resolve();
1038
1044
  }
1039
- return post('/ai/decide', { state: decisionState(state), questions: questions }).then(function (reply) {
1045
+ return post('/ai/decide', { state: decisionState(state), questions: questions }, AI_WAIT()).then(function (reply) {
1040
1046
  var answers = {};
1041
1047
  for (var k in (reply.answers || {})) if (reply.answers.hasOwnProperty(k)) answers[k] = decision(reply.answers[k]);
1042
1048
  if (key) saved[key] = { answers: reply.answers, until: now + o.remember * 1000 };
@@ -1 +1 @@
1
- {"name":"Zqx App Name","instance":"FE98C883-9417-425A-8B4A-689C3EB7CC0A","exe":"ZqxAppName","platform":"mac"}
1
+ {"name":"Zqx App Name","instance":"1F28D505-8715-4018-8ADA-A146F6FEB632","exe":"ZqxAppName","platform":"mac"}
@@ -11299,7 +11299,7 @@
11299
11299
  let reply = try await post("/ai/decide", [
11300
11300
  "state": try decisionState(state),
11301
11301
  "questions": questions.mapValues(\.json),
11302
- ])
11302
+ ], timeout: 615)
11303
11303
  let answers = reply["answers"] as? [String: [String: Any]] ?? [:]
11304
11304
  return answers.mapValues(LampDecision.init)
11305
11305
  }
@@ -11427,7 +11427,7 @@
11427
11427
  group.addTask {
11428
11428
  try Task.checkCancellation()
11429
11429
  do {
11430
- let reply = try await post("/ai/decide", body: request.body)
11430
+ let reply = try await post("/ai/decide", body: request.body, timeout: 615)
11431
11431
  guard let answers = reply["answers"] as? [String: Any], !answers.isEmpty else {
11432
11432
  return Outcome(index: request.index, error: "The decision model didn't answer.")
11433
11433
  }
@@ -11600,7 +11600,8 @@
11600
11600
  "images": images.compactMap(encode),
11601
11601
  ]
11602
11602
  if let model { body["model"] = model }
11603
- let reply = try await post("/ai", body)
11603
+ // As long as Lamp's guard on the call: a real answer is never cut off, however long the model takes.
11604
+ let reply = try await post("/ai", body, timeout: 615)
11604
11605
  guard let text = reply["text"] as? String else { throw Failure(message: "The AI request failed.") }
11605
11606
  return text
11606
11607
  }
@@ -301,6 +301,10 @@
301
301
  });
302
302
  }
303
303
 
304
+ // How long the page waits for an answer: as long as Lamp's guard on the call (10 minutes), or the page's own limit,
305
+ // with a little more for the answer to come back.
306
+ function AI_WAIT(seconds) { return (typeof seconds === 'number' && seconds > 0 ? Math.min(seconds, 600) : 600) * 1000 + 15000; }
307
+
304
308
  function post(path, body, timeout, deadline) {
305
309
  timeout = timeout || 240000;
306
310
  var done;
@@ -485,7 +489,9 @@
485
489
  return encode(o.images).then(function (images) {
486
490
  var body = { prompt: text, system: rules.filter(function (r) { return r; }).join('\n\n'), json: !!o.json, images: images };
487
491
  if (o.model) body.model = o.model;
488
- return post('/ai', body);
492
+ // No limit of the page's own unless it asks for one: Lamp's guard ends only a call that stopped answering.
493
+ if (typeof o.seconds === 'number' && o.seconds > 0) body.seconds = o.seconds;
494
+ return post('/ai', body, AI_WAIT(body.seconds));
489
495
  }).then(function (reply) {
490
496
  if (typeof reply.text !== 'string') throw new Failure('The AI request failed.');
491
497
  if (!o.json) return reply.text;
@@ -998,7 +1004,7 @@
998
1004
  },
999
1005
  /** Asks several questions about the same thing in one request. Answers come back under the same keys. */
1000
1006
  decide: function (about, questions) {
1001
- return post('/ai/decide', { state: decisionState(about), questions: questions }).then(function (reply) {
1007
+ return post('/ai/decide', { state: decisionState(about), questions: questions }, AI_WAIT()).then(function (reply) {
1002
1008
  var answers = {};
1003
1009
  for (var key in (reply.answers || {})) if (reply.answers.hasOwnProperty(key)) answers[key] = decision(reply.answers[key]);
1004
1010
  return answers;
@@ -1036,7 +1042,7 @@
1036
1042
  finish(i, answers);
1037
1043
  return Promise.resolve();
1038
1044
  }
1039
- return post('/ai/decide', { state: decisionState(state), questions: questions }).then(function (reply) {
1045
+ return post('/ai/decide', { state: decisionState(state), questions: questions }, AI_WAIT()).then(function (reply) {
1040
1046
  var answers = {};
1041
1047
  for (var k in (reply.answers || {})) if (reply.answers.hasOwnProperty(k)) answers[k] = decision(reply.answers[k]);
1042
1048
  if (key) saved[key] = { answers: reply.answers, until: now + o.remember * 1000 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lampdotdev/lamp",
3
- "version": "0.36.2-nightly.28",
3
+ "version": "0.36.2-nightly.29",
4
4
  "description": "Runs a web app or game from its folder and puts each saved change into the open page.",
5
5
  "type": "module",
6
6
  "bin": {