machineuser commited on
Commit
ca548dc
1 Parent(s): 803a700

Sync widgets demo

Browse files
packages/inference/README.md CHANGED
@@ -506,6 +506,21 @@ const gpt2 = hf.endpoint('https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/
506
  const { generated_text } = await gpt2.textGeneration({inputs: 'The answer to the universe is'});
507
  ```
508
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
509
  ## Running tests
510
 
511
  ```console
 
506
  const { generated_text } = await gpt2.textGeneration({inputs: 'The answer to the universe is'});
507
  ```
508
 
509
+ By default, all calls to the inference endpoint will wait until the model is
510
+ loaded. When [scaling to
511
+ 0](https://huggingface.co/docs/inference-endpoints/en/autoscaling#scaling-to-0)
512
+ is enabled on the endpoint, this can result in non-trivial waiting time. If
513
+ you'd rather disable this behavior and handle the endpoint's returned 500 HTTP
514
+ errors yourself, you can do so like so:
515
+
516
+ ```typescript
517
+ const gpt2 = hf.endpoint('https://xyz.eu-west-1.aws.endpoints.huggingface.cloud/gpt2');
518
+ const { generated_text } = await gpt2.textGeneration(
519
+ {inputs: 'The answer to the universe is'},
520
+ {retry_on_error: false},
521
+ );
522
+ ```
523
+
524
  ## Running tests
525
 
526
  ```console
packages/inference/package.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "name": "@huggingface/inference",
3
- "version": "2.6.4",
4
  "packageManager": "pnpm@8.10.5",
5
  "license": "MIT",
6
  "author": "Tim Mikeladze <tim.mikeladze@gmail.com>",
 
1
  {
2
  "name": "@huggingface/inference",
3
+ "version": "2.6.6",
4
  "packageManager": "pnpm@8.10.5",
5
  "license": "MIT",
6
  "author": "Tim Mikeladze <tim.mikeladze@gmail.com>",
packages/inference/src/lib/makeRequestOptions.ts CHANGED
@@ -27,7 +27,15 @@ export async function makeRequestOptions(
27
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
28
  const { accessToken, model: _model, ...otherArgs } = args;
29
  let { model } = args;
30
- const { forceTask: task, includeCredentials, taskHint, ...otherOptions } = options ?? {};
 
 
 
 
 
 
 
 
31
 
32
  const headers: Record<string, string> = {};
33
  if (accessToken) {
@@ -57,16 +65,16 @@ export async function makeRequestOptions(
57
 
58
  if (!binary) {
59
  headers["Content-Type"] = "application/json";
60
- } else {
61
- if (options?.wait_for_model) {
62
- headers["X-Wait-For-Model"] = "true";
63
- }
64
- if (options?.use_cache === false) {
65
- headers["X-Use-Cache"] = "false";
66
- }
67
- if (options?.dont_load_model) {
68
- headers["X-Load-Model"] = "0";
69
- }
70
  }
71
 
72
  const url = (() => {
 
27
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
28
  const { accessToken, model: _model, ...otherArgs } = args;
29
  let { model } = args;
30
+ const {
31
+ forceTask: task,
32
+ includeCredentials,
33
+ taskHint,
34
+ wait_for_model,
35
+ use_cache,
36
+ dont_load_model,
37
+ ...otherOptions
38
+ } = options ?? {};
39
 
40
  const headers: Record<string, string> = {};
41
  if (accessToken) {
 
65
 
66
  if (!binary) {
67
  headers["Content-Type"] = "application/json";
68
+ }
69
+
70
+ if (wait_for_model) {
71
+ headers["X-Wait-For-Model"] = "true";
72
+ }
73
+ if (use_cache === false) {
74
+ headers["X-Use-Cache"] = "false";
75
+ }
76
+ if (dont_load_model) {
77
+ headers["X-Load-Model"] = "0";
78
  }
79
 
80
  const url = (() => {
packages/inference/src/tasks/custom/streamingRequest.ts CHANGED
@@ -19,7 +19,7 @@ export async function* streamingRequest<T>(
19
  const response = await (options?.fetch ?? fetch)(url, info);
20
 
21
  if (options?.retry_on_error !== false && response.status === 503 && !options?.wait_for_model) {
22
- return streamingRequest(args, {
23
  ...options,
24
  wait_for_model: true,
25
  });
 
19
  const response = await (options?.fetch ?? fetch)(url, info);
20
 
21
  if (options?.retry_on_error !== false && response.status === 503 && !options?.wait_for_model) {
22
+ return yield* streamingRequest(args, {
23
  ...options,
24
  wait_for_model: true,
25
  });
packages/jinja/package.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "name": "@huggingface/jinja",
3
  "packageManager": "pnpm@8.10.5",
4
- "version": "0.2.1",
5
  "description": "A minimalistic JavaScript implementation of the Jinja templating engine, specifically designed for parsing and rendering ML chat templates.",
6
  "repository": "https://github.com/huggingface/huggingface.js.git",
7
  "publishConfig": {
 
1
  {
2
  "name": "@huggingface/jinja",
3
  "packageManager": "pnpm@8.10.5",
4
+ "version": "0.2.2",
5
  "description": "A minimalistic JavaScript implementation of the Jinja templating engine, specifically designed for parsing and rendering ML chat templates.",
6
  "repository": "https://github.com/huggingface/huggingface.js.git",
7
  "publishConfig": {
packages/jinja/src/ast.ts CHANGED
@@ -34,7 +34,7 @@ export class For extends Statement {
34
  override type = "For";
35
 
36
  constructor(
37
- public loopvar: Identifier,
38
  public iterable: Expression,
39
  public body: Statement[]
40
  ) {
@@ -136,6 +136,13 @@ export class ArrayLiteral extends Literal<Expression[]> {
136
  override type = "ArrayLiteral";
137
  }
138
 
 
 
 
 
 
 
 
139
  /**
140
  * Represents an object literal in the template.
141
  */
 
34
  override type = "For";
35
 
36
  constructor(
37
+ public loopvar: Identifier | TupleLiteral,
38
  public iterable: Expression,
39
  public body: Statement[]
40
  ) {
 
136
  override type = "ArrayLiteral";
137
  }
138
 
139
+ /**
140
+ * Represents a tuple literal in the template.
141
+ */
142
+ export class TupleLiteral extends Literal<Expression[]> {
143
+ override type = "TupleLiteral";
144
+ }
145
+
146
  /**
147
  * Represents an object literal in the template.
148
  */
packages/jinja/src/parser.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
  UnaryExpression,
21
  SliceExpression,
22
  KeywordArgumentExpression,
 
23
  } from "./ast";
24
 
25
  /**
@@ -172,16 +173,30 @@ export function parse(tokens: Token[]): Program {
172
  return new If(test, body, alternate);
173
  }
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  function parseForStatement(): For {
176
  // e.g., `message` in `for message in messages`
177
- const loopVariable = parsePrimaryExpression(); // should be an identifier
178
- if (!(loopVariable instanceof Identifier)) {
179
- throw new SyntaxError(`Expected identifier for the loop variable`);
180
  }
181
 
182
  expect(TOKEN_TYPES.In, "Expected `in` keyword following loop variable");
183
 
184
- // messages in `for message in messages`
185
  const iterable = parseExpression();
186
 
187
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
@@ -199,6 +214,10 @@ export function parse(tokens: Token[]): Program {
199
 
200
  function parseExpression(): Statement {
201
  // Choose parse function with lowest precedence
 
 
 
 
202
  const a = parseLogicalOrExpression();
203
  if (is(TOKEN_TYPES.If)) {
204
  // Ternary expression
@@ -464,9 +483,9 @@ export function parse(tokens: Token[]): Program {
464
  return new Identifier(token.value);
465
  case TOKEN_TYPES.OpenParen: {
466
  ++current; // consume opening parenthesis
467
- const expression = parseExpression();
468
  if (tokens[current].type !== TOKEN_TYPES.CloseParen) {
469
- throw new SyntaxError("Expected closing parenthesis");
470
  }
471
  ++current; // consume closing parenthesis
472
  return expression;
 
20
  UnaryExpression,
21
  SliceExpression,
22
  KeywordArgumentExpression,
23
+ TupleLiteral,
24
  } from "./ast";
25
 
26
  /**
 
173
  return new If(test, body, alternate);
174
  }
175
 
176
+ function parseExpressionSequence(primary = false): Statement {
177
+ const fn = primary ? parsePrimaryExpression : parseExpression;
178
+ const expressions = [fn()];
179
+ const isTuple = is(TOKEN_TYPES.Comma);
180
+ while (isTuple) {
181
+ ++current; // consume comma
182
+ expressions.push(fn());
183
+ if (!is(TOKEN_TYPES.Comma)) {
184
+ break;
185
+ }
186
+ }
187
+ return isTuple ? new TupleLiteral(expressions) : expressions[0];
188
+ }
189
+
190
  function parseForStatement(): For {
191
  // e.g., `message` in `for message in messages`
192
+ const loopVariable = parseExpressionSequence(true); // should be an identifier
193
+ if (!(loopVariable instanceof Identifier || loopVariable instanceof TupleLiteral)) {
194
+ throw new SyntaxError(`Expected identifier/tuple for the loop variable, got ${loopVariable.type} instead`);
195
  }
196
 
197
  expect(TOKEN_TYPES.In, "Expected `in` keyword following loop variable");
198
 
199
+ // `messages` in `for message in messages`
200
  const iterable = parseExpression();
201
 
202
  expect(TOKEN_TYPES.CloseStatement, "Expected closing statement token");
 
214
 
215
  function parseExpression(): Statement {
216
  // Choose parse function with lowest precedence
217
+ return parseTernaryExpression();
218
+ }
219
+
220
+ function parseTernaryExpression(): Statement {
221
  const a = parseLogicalOrExpression();
222
  if (is(TOKEN_TYPES.If)) {
223
  // Ternary expression
 
483
  return new Identifier(token.value);
484
  case TOKEN_TYPES.OpenParen: {
485
  ++current; // consume opening parenthesis
486
+ const expression = parseExpressionSequence();
487
  if (tokens[current].type !== TOKEN_TYPES.CloseParen) {
488
+ throw new SyntaxError(`Expected closing parenthesis, got ${tokens[current].type} instead`);
489
  }
490
  ++current; // consume closing parenthesis
491
  return expression;
packages/jinja/src/runtime.ts CHANGED
@@ -18,6 +18,7 @@ import type {
18
  SliceExpression,
19
  KeywordArgumentExpression,
20
  ObjectLiteral,
 
21
  } from "./ast";
22
  import { slice, titleCase } from "./utils";
23
 
@@ -138,6 +139,14 @@ export class ObjectValue extends RuntimeValue<Map<string, AnyRuntimeValue>> {
138
  return this.value.get(key.value) ?? defaultValue ?? new NullValue();
139
  }),
140
  ],
 
 
 
 
 
 
 
 
141
  ]);
142
  }
143
 
@@ -161,6 +170,14 @@ export class ArrayValue extends RuntimeValue<AnyRuntimeValue[]> {
161
  }
162
  }
163
 
 
 
 
 
 
 
 
 
164
  /**
165
  * Represents a Function value at runtime.
166
  */
@@ -500,6 +517,17 @@ export class Interpreter {
500
  default:
501
  throw new Error(`Unknown NumericValue filter: ${filter.value}`);
502
  }
 
 
 
 
 
 
 
 
 
 
 
503
  }
504
  throw new Error(`Cannot apply filter "${filter.value}" to type: ${operand.type}`);
505
  } else if (node.filter.type === "CallExpression") {
@@ -763,8 +791,29 @@ export class Interpreter {
763
 
764
  scope.setVariable("loop", new ObjectValue(loop));
765
 
 
 
766
  // For this iteration, set the loop variable to the current element
767
- scope.setVariable(node.loopvar.value, iterable.value[i]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
768
 
769
  // Evaluate the body of the for loop
770
  const evaluated = this.evaluateBlock(node.body, scope);
@@ -799,6 +848,8 @@ export class Interpreter {
799
  return new BooleanValue((statement as BooleanLiteral).value);
800
  case "ArrayLiteral":
801
  return new ArrayValue((statement as ArrayLiteral).value.map((x) => this.evaluate(x, environment)));
 
 
802
  case "ObjectLiteral": {
803
  const mapping = new Map();
804
  for (const [key, value] of (statement as ObjectLiteral).value) {
 
18
  SliceExpression,
19
  KeywordArgumentExpression,
20
  ObjectLiteral,
21
+ TupleLiteral,
22
  } from "./ast";
23
  import { slice, titleCase } from "./utils";
24
 
 
139
  return this.value.get(key.value) ?? defaultValue ?? new NullValue();
140
  }),
141
  ],
142
+ [
143
+ "items",
144
+ new FunctionValue(() => {
145
+ return new ArrayValue(
146
+ Array.from(this.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
147
+ );
148
+ }),
149
+ ],
150
  ]);
151
  }
152
 
 
170
  }
171
  }
172
 
173
+ /**
174
+ * Represents a Tuple value at runtime.
175
+ * NOTE: We extend ArrayValue since JavaScript does not have a built-in Tuple type.
176
+ */
177
+ export class TupleValue extends ArrayValue {
178
+ override type = "TupleValue";
179
+ }
180
+
181
  /**
182
  * Represents a Function value at runtime.
183
  */
 
517
  default:
518
  throw new Error(`Unknown NumericValue filter: ${filter.value}`);
519
  }
520
+ } else if (operand instanceof ObjectValue) {
521
+ switch (filter.value) {
522
+ case "items":
523
+ return new ArrayValue(
524
+ Array.from(operand.value.entries()).map(([key, value]) => new ArrayValue([new StringValue(key), value]))
525
+ );
526
+ case "length":
527
+ return new NumericValue(operand.value.size);
528
+ default:
529
+ throw new Error(`Unknown ObjectValue filter: ${filter.value}`);
530
+ }
531
  }
532
  throw new Error(`Cannot apply filter "${filter.value}" to type: ${operand.type}`);
533
  } else if (node.filter.type === "CallExpression") {
 
791
 
792
  scope.setVariable("loop", new ObjectValue(loop));
793
 
794
+ const current = iterable.value[i];
795
+
796
  // For this iteration, set the loop variable to the current element
797
+ if (node.loopvar.type === "Identifier") {
798
+ scope.setVariable((node.loopvar as Identifier).value, current);
799
+ } else if (node.loopvar.type === "TupleLiteral") {
800
+ const loopvar = node.loopvar as TupleLiteral;
801
+ if (current.type !== "ArrayValue") {
802
+ throw new Error(`Cannot unpack non-iterable type: ${current.type}`);
803
+ }
804
+ const c = current as ArrayValue;
805
+
806
+ // check if too few or many items to unpack
807
+ if (loopvar.value.length !== c.value.length) {
808
+ throw new Error(`Too ${loopvar.value.length > c.value.length ? "few" : "many"} items to unpack`);
809
+ }
810
+ for (let j = 0; j < loopvar.value.length; ++j) {
811
+ if (loopvar.value[j].type !== "Identifier") {
812
+ throw new Error(`Cannot unpack non-identifier type: ${loopvar.value[j].type}`);
813
+ }
814
+ scope.setVariable((loopvar.value[j] as Identifier).value, c.value[j]);
815
+ }
816
+ }
817
 
818
  // Evaluate the body of the for loop
819
  const evaluated = this.evaluateBlock(node.body, scope);
 
848
  return new BooleanValue((statement as BooleanLiteral).value);
849
  case "ArrayLiteral":
850
  return new ArrayValue((statement as ArrayLiteral).value.map((x) => this.evaluate(x, environment)));
851
+ case "TupleLiteral":
852
+ return new TupleValue((statement as TupleLiteral).value.map((x) => this.evaluate(x, environment)));
853
  case "ObjectLiteral": {
854
  const mapping = new Map();
855
  for (const [key, value] of (statement as ObjectLiteral).value) {
packages/jinja/test/e2e.test.js CHANGED
@@ -376,6 +376,46 @@ const TEST_CUSTOM_TEMPLATES = Object.freeze({
376
  // to be repeated twice. We replicate this behaviour here.
377
  target: `You are a friendly chatbot who always responds in the style of a pirateYou are a friendly chatbot who always responds in the style of a pirate### Instruction: Hello, how are you?### Response: I'm doing great. How can I help you today?### Instruction: I'd like to show off how chat templating works!`,
378
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  });
380
 
381
  describe("End-to-end tests", () => {
 
376
  // to be repeated twice. We replicate this behaviour here.
377
  target: `You are a friendly chatbot who always responds in the style of a pirateYou are a friendly chatbot who always responds in the style of a pirate### Instruction: Hello, how are you?### Response: I'm doing great. How can I help you today?### Instruction: I'd like to show off how chat templating works!`,
378
  },
379
+ "CohereForAI/c4ai-command-r-v01": {
380
+ chat_template:
381
+ `{{ bos_token }}{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% set system_message = '## Task and Context\\nYou help people answer their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You should focus on serving the user\\'s needs as best you can, which will be wide-ranging.\\n\\n## Style Guide\\nUnless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling.' %}{% endif %}` +
382
+ `{{ '<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>' }}{{ '# Safety Preamble' }}{{ '\nThe instructions in this section override those in the task description and style guide sections. Don\\'t answer questions that are harmful or immoral.' }}{{ '\n\n# System Preamble' }}{{ '\n## Basic Rules' }}{{ '\nYou are a powerful conversational AI trained by Cohere to help people. You are augmented by a number of tools, and your job is to use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see a specific instruction instructing you what kind of response to generate. When you answer the user\\'s requests, you cite your sources in your answers, according to those instructions.' }}` +
383
+ `{{ '\n\n# User Preamble' }}{{ '\n' + system_message }}` +
384
+ `{{'\n\n## Available Tools\nHere is a list of tools that you have available to you:\n\n'}}{% for tool in tools %}{% if loop.index0 != 0 %}{{ '\n\n'}}{% endif %}{{'\`\`\`python\ndef ' + tool.name + '('}}{% for param_name, param_fields in tool.parameter_definitions.items() %}{% if loop.index0 != 0 %}{{ ', '}}{% endif %}{{param_name}}: {% if not param_fields.required %}{{'Optional[' + param_fields.type + '] = None'}}{% else %}{{ param_fields.type }}{% endif %}{% endfor %}{{ ') -> List[Dict]:\n """'}}{{ tool.description }}{% if tool.parameter_definitions|length != 0 %}{{ '\n\n Args:\n '}}{% for param_name, param_fields in tool.parameter_definitions.items() %}{% if loop.index0 != 0 %}{{ '\n ' }}{% endif %}{{ param_name + ' ('}}{% if not param_fields.required %}{{'Optional[' + param_fields.type + ']'}}{% else %}{{ param_fields.type }}{% endif %}{{ '): ' + param_fields.description }}{% endfor %}{% endif %}{{ '\n """\n pass\n\`\`\`' }}{% endfor %}{{ '<|END_OF_TURN_TOKEN|>'}}` +
385
+ `{% for message in loop_messages %}{% set content = message['content'] %}{% if message['role'] == 'user' %}{{ '<|START_OF_TURN_TOKEN|><|USER_TOKEN|>' + content.strip() + '<|END_OF_TURN_TOKEN|>' }}{% elif message['role'] == 'system' %}{{ '<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>' + content.strip() + '<|END_OF_TURN_TOKEN|>' }}{% elif message['role'] == 'assistant' %}{{ '<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>' + content.strip() + '<|END_OF_TURN_TOKEN|>' }}{% endif %}{% endfor %}` +
386
+ `{{'<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>Write \\'Action:\\' followed by a json-formatted list of actions that you want to perform in order to produce a good response to the user\\'s last input. You can use any of the supplied tools any number of times, but you should aim to execute the minimum number of necessary actions for the input. You should use the \`directly-answer\` tool if calling the other tools is unnecessary. The list of actions you want to call should be formatted as a list of json objects, for example:\n\`\`\`json\n[\n {\n "tool_name": title of the tool in the specification,\n "parameters": a dict of parameters to input into the tool as they are defined in the specs, or {} if it takes no parameters\n }\n]\`\`\`<|END_OF_TURN_TOKEN|>'}}{% if add_generation_prompt %}{{ '<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>' }}{% endif %}`,
387
+
388
+ data: {
389
+ messages: [{ role: "user", content: "Whats the biggest penguin in the world?" }],
390
+ tools: [
391
+ {
392
+ name: "internet_search",
393
+ description: "Returns a list of relevant document snippets for a textual query retrieved from the internet",
394
+ parameter_definitions: {
395
+ query: {
396
+ description: "Query to search the internet with",
397
+ type: "str",
398
+ required: true,
399
+ },
400
+ },
401
+ },
402
+ {
403
+ name: "directly_answer",
404
+ description:
405
+ "Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history",
406
+ parameter_definitions: {},
407
+ },
408
+ ],
409
+ bos_token: "<BOS_TOKEN>",
410
+ add_generation_prompt: true,
411
+ },
412
+ target:
413
+ "<BOS_TOKEN><|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|># Safety Preamble\nThe instructions in this section override those in the task description and style guide sections. Don't answer questions that are harmful or immoral.\n\n# System Preamble\n## Basic Rules\nYou are a powerful conversational AI trained by Cohere to help people. You are augmented by a number of tools, and your job is to use and consume the output of these tools to best help the user. You will see a conversation history between yourself and a user, ending with an utterance from the user. You will then see a specific instruction instructing you what kind of response to generate. When you answer the user's requests, you cite your sources in your answers, according to those instructions." +
414
+ "\n\n# User Preamble\n## Task and Context\nYou help people answer their questions and other requests interactively. You will be asked a very wide array of requests on all kinds of topics. You will be equipped with a wide range of search engines or similar tools to help you, which you use to research your answer. You should focus on serving the user's needs as best you can, which will be wide-ranging.\n\n## Style Guide\nUnless the user asks for a different style of answer, you should answer in full sentences, using proper grammar and spelling." +
415
+ '\n\n## Available Tools\nHere is a list of tools that you have available to you:\n\n```python\ndef internet_search(query: str) -> List[Dict]:\n """Returns a list of relevant document snippets for a textual query retrieved from the internet\n\n Args:\n query (str): Query to search the internet with\n """\n pass\n```\n\n```python\ndef directly_answer() -> List[Dict]:\n """Calls a standard (un-augmented) AI chatbot to generate a response given the conversation history\n """\n pass\n```<|END_OF_TURN_TOKEN|>' +
416
+ "<|START_OF_TURN_TOKEN|><|USER_TOKEN|>Whats the biggest penguin in the world?<|END_OF_TURN_TOKEN|>" +
417
+ '<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>Write \'Action:\' followed by a json-formatted list of actions that you want to perform in order to produce a good response to the user\'s last input. You can use any of the supplied tools any number of times, but you should aim to execute the minimum number of necessary actions for the input. You should use the `directly-answer` tool if calling the other tools is unnecessary. The list of actions you want to call should be formatted as a list of json objects, for example:\n```json\n[\n {\n "tool_name": title of the tool in the specification,\n "parameters": a dict of parameters to input into the tool as they are defined in the specs, or {} if it takes no parameters\n }\n]```<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>',
418
+ },
419
  });
420
 
421
  describe("End-to-end tests", () => {
packages/jinja/test/templates.test.js CHANGED
@@ -27,6 +27,7 @@ const TEST_STRINGS = {
27
 
28
  // For loops
29
  FOR_LOOP: `{% for message in messages %}{{ message['content'] }}{% endfor %}`,
 
30
 
31
  // Set variables
32
  VARIABLES: `{% set x = 'Hello' %}{% set y = 'World' %}{{ x + ' ' + y }}`,
@@ -76,6 +77,7 @@ const TEST_STRINGS = {
76
  FILTER_OPERATOR_3: `|{{ -1 | abs }}|{{ 1 | abs }}|`,
77
  FILTER_OPERATOR_4: `{{ items | selectattr('key') | length }}`,
78
  FILTER_OPERATOR_5: `{{ messages | selectattr('role', 'equalto', 'system') | length }}`,
 
79
 
80
  // Logical operators between non-Booleans
81
  BOOLEAN_NUMERICAL: `|{{ 1 and 2 }}|{{ 1 and 0 }}|{{ 0 and 1 }}|{{ 0 and 0 }}|{{ 1 or 2 }}|{{ 1 or 0 }}|{{ 0 or 1 }}|{{ 0 or 0 }}|{{ not 1 }}|{{ not 0 }}|`,
@@ -103,6 +105,7 @@ const TEST_STRINGS = {
103
  // Object operators
104
  OBJECT_OPERATORS: `|{{ 'known' in obj }}|{{ 'known' not in obj }}|{{ 'unknown' in obj }}|{{ 'unknown' not in obj }}|`,
105
  OBJECT_OPERATORS_1: `|{{ obj.get('known') }}|{{ obj.get('unknown') is none }}|{{ obj.get('unknown') is defined }}|`,
 
106
 
107
  // Scope
108
  SCOPE: `{% set ns = namespace(found=false) %}{% for num in nums %}{% if num == 1 %}{{ 'found=' }}{% set ns.found = true %}{% endif %}{% endfor %}{{ ns.found }}`,
@@ -118,6 +121,9 @@ const TEST_STRINGS = {
118
  // Array literals
119
  ARRAY_LITERALS: `{{ [1, true, 'hello', [1, 2, 3, 4], var] | length }}`,
120
 
 
 
 
121
  // Object literals
122
  OBJECT_LITERALS: `{{ { 'key': 'value', key: 'value2', "key3": [1, {'foo': 'bar'} ] }['key'] }}`,
123
 
@@ -520,6 +526,42 @@ const TEST_PARSED = {
520
  { value: "endfor", type: "EndFor" },
521
  { value: "%}", type: "CloseStatement" },
522
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
523
 
524
  // Set variables
525
  VARIABLES: [
@@ -1343,6 +1385,29 @@ const TEST_PARSED = {
1343
  { value: "length", type: "Identifier" },
1344
  { value: "}}", type: "CloseExpression" },
1345
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1346
 
1347
  // Logical operators between non-Booleans
1348
  BOOLEAN_NUMERICAL: [
@@ -1952,6 +2017,34 @@ const TEST_PARSED = {
1952
  { value: "}}", type: "CloseExpression" },
1953
  { value: "|", type: "Text" },
1954
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1955
 
1956
  // Scope
1957
  SCOPE: [
@@ -2132,6 +2225,23 @@ const TEST_PARSED = {
2132
  { value: "}}", type: "CloseExpression" },
2133
  ],
2134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2135
  // Object literals
2136
  OBJECT_LITERALS: [
2137
  { value: "{{", type: "OpenExpression" },
@@ -2217,6 +2327,7 @@ const TEST_CONTEXT = {
2217
  { role: "user", content: "C" },
2218
  ],
2219
  },
 
2220
 
2221
  // Set variables
2222
  VARIABLES: {},
@@ -2304,6 +2415,9 @@ const TEST_CONTEXT = {
2304
  FILTER_OPERATOR_5: {
2305
  messages: [{ role: "system" }, { role: "user" }, { role: "assistant" }],
2306
  },
 
 
 
2307
 
2308
  // Logical operators between non-Booleans
2309
  BOOLEAN_NUMERICAL: {},
@@ -2343,6 +2457,9 @@ const TEST_CONTEXT = {
2343
  known: true,
2344
  },
2345
  },
 
 
 
2346
 
2347
  // Scope
2348
  SCOPE: { nums: [1, 2, 3] },
@@ -2358,6 +2475,9 @@ const TEST_CONTEXT = {
2358
  // Array literals
2359
  ARRAY_LITERALS: { var: true },
2360
 
 
 
 
2361
  // Object literals
2362
  OBJECT_LITERALS: {
2363
  key: "key2",
@@ -2390,6 +2510,7 @@ const EXPECTED_OUTPUTS = {
2390
 
2391
  // For loops
2392
  FOR_LOOP: "ABC",
 
2393
 
2394
  // Set variables
2395
  VARIABLES: "Hello World",
@@ -2439,6 +2560,7 @@ const EXPECTED_OUTPUTS = {
2439
  FILTER_OPERATOR_3: `|1|1|`,
2440
  FILTER_OPERATOR_4: `2`,
2441
  FILTER_OPERATOR_5: `1`,
 
2442
 
2443
  // Logical operators between non-Booleans
2444
  BOOLEAN_NUMERICAL: `|2|0|0|0|1|1|1|0|false|true|`,
@@ -2466,6 +2588,7 @@ const EXPECTED_OUTPUTS = {
2466
  // Object operators
2467
  OBJECT_OPERATORS: `|true|false|false|true|`,
2468
  OBJECT_OPERATORS_1: `|true|true|true|`,
 
2469
 
2470
  // Scope
2471
  SCOPE: `found=true`,
@@ -2481,6 +2604,9 @@ const EXPECTED_OUTPUTS = {
2481
  // Array literals
2482
  ARRAY_LITERALS: `5`,
2483
 
 
 
 
2484
  // Object literals
2485
  OBJECT_LITERALS: `value`,
2486
 
 
27
 
28
  // For loops
29
  FOR_LOOP: `{% for message in messages %}{{ message['content'] }}{% endfor %}`,
30
+ FOR_LOOP_UNPACKING: `|{% for x, y in [ [1, 2], [3, 4] ] %}|{{ x + ' ' + y }}|{% endfor %}|`,
31
 
32
  // Set variables
33
  VARIABLES: `{% set x = 'Hello' %}{% set y = 'World' %}{{ x + ' ' + y }}`,
 
77
  FILTER_OPERATOR_3: `|{{ -1 | abs }}|{{ 1 | abs }}|`,
78
  FILTER_OPERATOR_4: `{{ items | selectattr('key') | length }}`,
79
  FILTER_OPERATOR_5: `{{ messages | selectattr('role', 'equalto', 'system') | length }}`,
80
+ FILTER_OPERATOR_6: `|{{ obj | length }}|{{ (obj | items)[1:] | length }}|`,
81
 
82
  // Logical operators between non-Booleans
83
  BOOLEAN_NUMERICAL: `|{{ 1 and 2 }}|{{ 1 and 0 }}|{{ 0 and 1 }}|{{ 0 and 0 }}|{{ 1 or 2 }}|{{ 1 or 0 }}|{{ 0 or 1 }}|{{ 0 or 0 }}|{{ not 1 }}|{{ not 0 }}|`,
 
105
  // Object operators
106
  OBJECT_OPERATORS: `|{{ 'known' in obj }}|{{ 'known' not in obj }}|{{ 'unknown' in obj }}|{{ 'unknown' not in obj }}|`,
107
  OBJECT_OPERATORS_1: `|{{ obj.get('known') }}|{{ obj.get('unknown') is none }}|{{ obj.get('unknown') is defined }}|`,
108
+ OBJECT_OPERATORS_2: `|{% for x, y in obj.items() %}|{{ x + ' ' + y }}|{% endfor %}|`,
109
 
110
  // Scope
111
  SCOPE: `{% set ns = namespace(found=false) %}{% for num in nums %}{% if num == 1 %}{{ 'found=' }}{% set ns.found = true %}{% endif %}{% endfor %}{{ ns.found }}`,
 
121
  // Array literals
122
  ARRAY_LITERALS: `{{ [1, true, 'hello', [1, 2, 3, 4], var] | length }}`,
123
 
124
+ // Tuple literals
125
+ TUPLE_LITERALS: `{{ (1, (1, 2)) | length }}`,
126
+
127
  // Object literals
128
  OBJECT_LITERALS: `{{ { 'key': 'value', key: 'value2', "key3": [1, {'foo': 'bar'} ] }['key'] }}`,
129
 
 
526
  { value: "endfor", type: "EndFor" },
527
  { value: "%}", type: "CloseStatement" },
528
  ],
529
+ FOR_LOOP_UNPACKING: [
530
+ { value: "|", type: "Text" },
531
+ { value: "{%", type: "OpenStatement" },
532
+ { value: "for", type: "For" },
533
+ { value: "x", type: "Identifier" },
534
+ { value: ",", type: "Comma" },
535
+ { value: "y", type: "Identifier" },
536
+ { value: "in", type: "In" },
537
+ { value: "[", type: "OpenSquareBracket" },
538
+ { value: "[", type: "OpenSquareBracket" },
539
+ { value: "1", type: "NumericLiteral" },
540
+ { value: ",", type: "Comma" },
541
+ { value: "2", type: "NumericLiteral" },
542
+ { value: "]", type: "CloseSquareBracket" },
543
+ { value: ",", type: "Comma" },
544
+ { value: "[", type: "OpenSquareBracket" },
545
+ { value: "3", type: "NumericLiteral" },
546
+ { value: ",", type: "Comma" },
547
+ { value: "4", type: "NumericLiteral" },
548
+ { value: "]", type: "CloseSquareBracket" },
549
+ { value: "]", type: "CloseSquareBracket" },
550
+ { value: "%}", type: "CloseStatement" },
551
+ { value: "|", type: "Text" },
552
+ { value: "{{", type: "OpenExpression" },
553
+ { value: "x", type: "Identifier" },
554
+ { value: "+", type: "AdditiveBinaryOperator" },
555
+ { value: " ", type: "StringLiteral" },
556
+ { value: "+", type: "AdditiveBinaryOperator" },
557
+ { value: "y", type: "Identifier" },
558
+ { value: "}}", type: "CloseExpression" },
559
+ { value: "|", type: "Text" },
560
+ { value: "{%", type: "OpenStatement" },
561
+ { value: "endfor", type: "EndFor" },
562
+ { value: "%}", type: "CloseStatement" },
563
+ { value: "|", type: "Text" },
564
+ ],
565
 
566
  // Set variables
567
  VARIABLES: [
 
1385
  { value: "length", type: "Identifier" },
1386
  { value: "}}", type: "CloseExpression" },
1387
  ],
1388
+ FILTER_OPERATOR_6: [
1389
+ { value: "|", type: "Text" },
1390
+ { value: "{{", type: "OpenExpression" },
1391
+ { value: "obj", type: "Identifier" },
1392
+ { value: "|", type: "Pipe" },
1393
+ { value: "length", type: "Identifier" },
1394
+ { value: "}}", type: "CloseExpression" },
1395
+ { value: "|", type: "Text" },
1396
+ { value: "{{", type: "OpenExpression" },
1397
+ { value: "(", type: "OpenParen" },
1398
+ { value: "obj", type: "Identifier" },
1399
+ { value: "|", type: "Pipe" },
1400
+ { value: "items", type: "Identifier" },
1401
+ { value: ")", type: "CloseParen" },
1402
+ { value: "[", type: "OpenSquareBracket" },
1403
+ { value: "1", type: "NumericLiteral" },
1404
+ { value: ":", type: "Colon" },
1405
+ { value: "]", type: "CloseSquareBracket" },
1406
+ { value: "|", type: "Pipe" },
1407
+ { value: "length", type: "Identifier" },
1408
+ { value: "}}", type: "CloseExpression" },
1409
+ { value: "|", type: "Text" },
1410
+ ],
1411
 
1412
  // Logical operators between non-Booleans
1413
  BOOLEAN_NUMERICAL: [
 
2017
  { value: "}}", type: "CloseExpression" },
2018
  { value: "|", type: "Text" },
2019
  ],
2020
+ OBJECT_OPERATORS_2: [
2021
+ { value: "|", type: "Text" },
2022
+ { value: "{%", type: "OpenStatement" },
2023
+ { value: "for", type: "For" },
2024
+ { value: "x", type: "Identifier" },
2025
+ { value: ",", type: "Comma" },
2026
+ { value: "y", type: "Identifier" },
2027
+ { value: "in", type: "In" },
2028
+ { value: "obj", type: "Identifier" },
2029
+ { value: ".", type: "Dot" },
2030
+ { value: "items", type: "Identifier" },
2031
+ { value: "(", type: "OpenParen" },
2032
+ { value: ")", type: "CloseParen" },
2033
+ { value: "%}", type: "CloseStatement" },
2034
+ { value: "|", type: "Text" },
2035
+ { value: "{{", type: "OpenExpression" },
2036
+ { value: "x", type: "Identifier" },
2037
+ { value: "+", type: "AdditiveBinaryOperator" },
2038
+ { value: " ", type: "StringLiteral" },
2039
+ { value: "+", type: "AdditiveBinaryOperator" },
2040
+ { value: "y", type: "Identifier" },
2041
+ { value: "}}", type: "CloseExpression" },
2042
+ { value: "|", type: "Text" },
2043
+ { value: "{%", type: "OpenStatement" },
2044
+ { value: "endfor", type: "EndFor" },
2045
+ { value: "%}", type: "CloseStatement" },
2046
+ { value: "|", type: "Text" },
2047
+ ],
2048
 
2049
  // Scope
2050
  SCOPE: [
 
2225
  { value: "}}", type: "CloseExpression" },
2226
  ],
2227
 
2228
+ // Tuple literals
2229
+ TUPLE_LITERALS: [
2230
+ { value: "{{", type: "OpenExpression" },
2231
+ { value: "(", type: "OpenParen" },
2232
+ { value: "1", type: "NumericLiteral" },
2233
+ { value: ",", type: "Comma" },
2234
+ { value: "(", type: "OpenParen" },
2235
+ { value: "1", type: "NumericLiteral" },
2236
+ { value: ",", type: "Comma" },
2237
+ { value: "2", type: "NumericLiteral" },
2238
+ { value: ")", type: "CloseParen" },
2239
+ { value: ")", type: "CloseParen" },
2240
+ { value: "|", type: "Pipe" },
2241
+ { value: "length", type: "Identifier" },
2242
+ { value: "}}", type: "CloseExpression" },
2243
+ ],
2244
+
2245
  // Object literals
2246
  OBJECT_LITERALS: [
2247
  { value: "{{", type: "OpenExpression" },
 
2327
  { role: "user", content: "C" },
2328
  ],
2329
  },
2330
+ FOR_LOOP_UNPACKING: {},
2331
 
2332
  // Set variables
2333
  VARIABLES: {},
 
2415
  FILTER_OPERATOR_5: {
2416
  messages: [{ role: "system" }, { role: "user" }, { role: "assistant" }],
2417
  },
2418
+ FILTER_OPERATOR_6: {
2419
+ obj: { a: 1, b: 2, c: 3 },
2420
+ },
2421
 
2422
  // Logical operators between non-Booleans
2423
  BOOLEAN_NUMERICAL: {},
 
2457
  known: true,
2458
  },
2459
  },
2460
+ OBJECT_OPERATORS_2: {
2461
+ obj: { a: 1, b: 2, c: 3 },
2462
+ },
2463
 
2464
  // Scope
2465
  SCOPE: { nums: [1, 2, 3] },
 
2475
  // Array literals
2476
  ARRAY_LITERALS: { var: true },
2477
 
2478
+ // Tuple literals
2479
+ TUPLE_LITERALS: {},
2480
+
2481
  // Object literals
2482
  OBJECT_LITERALS: {
2483
  key: "key2",
 
2510
 
2511
  // For loops
2512
  FOR_LOOP: "ABC",
2513
+ FOR_LOOP_UNPACKING: "||1 2||3 4||",
2514
 
2515
  // Set variables
2516
  VARIABLES: "Hello World",
 
2560
  FILTER_OPERATOR_3: `|1|1|`,
2561
  FILTER_OPERATOR_4: `2`,
2562
  FILTER_OPERATOR_5: `1`,
2563
+ FILTER_OPERATOR_6: `|3|2|`,
2564
 
2565
  // Logical operators between non-Booleans
2566
  BOOLEAN_NUMERICAL: `|2|0|0|0|1|1|1|0|false|true|`,
 
2588
  // Object operators
2589
  OBJECT_OPERATORS: `|true|false|false|true|`,
2590
  OBJECT_OPERATORS_1: `|true|true|true|`,
2591
+ OBJECT_OPERATORS_2: `||a 1||b 2||c 3||`,
2592
 
2593
  // Scope
2594
  SCOPE: `found=true`,
 
2604
  // Array literals
2605
  ARRAY_LITERALS: `5`,
2606
 
2607
+ // Tuple literals
2608
+ TUPLE_LITERALS: `2`,
2609
+
2610
  // Object literals
2611
  OBJECT_LITERALS: `value`,
2612
 
packages/tasks/src/model-libraries.ts CHANGED
@@ -202,11 +202,11 @@ export const MODEL_LIBRARIES_UI_ELEMENTS = {
202
  snippets: snippets.mlx,
203
  filter: true,
204
  },
205
- mlxim: {
206
- prettyLabel: "mlxim",
207
  repoName: "mlx-image",
208
  repoUrl: "https://github.com/riccardomusmeci/mlx-image",
209
- docsUrl: "https://huggingface.co/docs/hub/mlxim",
210
  snippets: snippets.mlxim,
211
  filter: false,
212
  countDownloads: { term: { path: "model.safetensors" } },
 
202
  snippets: snippets.mlx,
203
  filter: true,
204
  },
205
+ "mlx-image": {
206
+ prettyLabel: "mlx-image",
207
  repoName: "mlx-image",
208
  repoUrl: "https://github.com/riccardomusmeci/mlx-image",
209
+ docsUrl: "https://huggingface.co/docs/hub/mlx-image",
210
  snippets: snippets.mlxim,
211
  filter: false,
212
  countDownloads: { term: { path: "model.safetensors" } },