llaminate 0.1.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.
Files changed (72) hide show
  1. package/.gitattributes +2 -0
  2. package/LICENSE +21 -0
  3. package/README.md +95 -0
  4. package/assets/llaminate-256.webp +0 -0
  5. package/assets/llaminate.jpg +0 -0
  6. package/assets/llaminate.webp +0 -0
  7. package/dist/build-info.json +5 -0
  8. package/dist/config.schema.json +126 -0
  9. package/dist/llaminate.d.ts +202 -0
  10. package/dist/llaminate.min.js +9 -0
  11. package/dist/llaminate.min.js.map +1 -0
  12. package/dist/ratelimiter.d.ts +33 -0
  13. package/dist/ratelimiter.min.js +7 -0
  14. package/dist/ratelimiter.min.js.map +1 -0
  15. package/dist/system.hbs +3 -0
  16. package/docs/LICENSE +21 -0
  17. package/docs/Llaminate.html +1040 -0
  18. package/docs/Llaminate.module_Endpoints.html +278 -0
  19. package/docs/Llaminate.module_Llaminate.html +200 -0
  20. package/docs/Llaminate.module_MimeTypes.html +275 -0
  21. package/docs/LlaminateConfig.html +667 -0
  22. package/docs/LlaminateMessage.html +328 -0
  23. package/docs/LlaminateResponse.html +253 -0
  24. package/docs/assets/llaminate-256.webp +0 -0
  25. package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
  26. package/docs/fonts/OpenSans-Bold-webfont.svg +1830 -0
  27. package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
  28. package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
  29. package/docs/fonts/OpenSans-BoldItalic-webfont.svg +1830 -0
  30. package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
  31. package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
  32. package/docs/fonts/OpenSans-Italic-webfont.svg +1830 -0
  33. package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
  34. package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
  35. package/docs/fonts/OpenSans-Light-webfont.svg +1831 -0
  36. package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
  37. package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
  38. package/docs/fonts/OpenSans-LightItalic-webfont.svg +1835 -0
  39. package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
  40. package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
  41. package/docs/fonts/OpenSans-Regular-webfont.svg +1831 -0
  42. package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
  43. package/docs/index.html +142 -0
  44. package/docs/scripts/linenumber.js +25 -0
  45. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  46. package/docs/scripts/prettify/lang-css.js +2 -0
  47. package/docs/scripts/prettify/prettify.js +28 -0
  48. package/docs/styles/jsdoc-default.css +358 -0
  49. package/docs/styles/prettify-jsdoc.css +111 -0
  50. package/docs/styles/prettify-tomorrow.css +132 -0
  51. package/jsdoc.json +23 -0
  52. package/package.json +38 -0
  53. package/scripts/build.sh +21 -0
  54. package/scripts/docs.sh +28 -0
  55. package/scripts/prebuild.js +43 -0
  56. package/scripts/prepare.sh +11 -0
  57. package/scripts/pretest.js +14 -0
  58. package/src/config.schema.json +126 -0
  59. package/src/llaminate.d.ts +99 -0
  60. package/src/llaminate.ts +1326 -0
  61. package/src/llaminate.types.js +176 -0
  62. package/src/ratelimiter.ts +95 -0
  63. package/src/system.hbs +3 -0
  64. package/tests/attachments.test.js +66 -0
  65. package/tests/common/base64.js +13 -0
  66. package/tests/common/matches.js +69 -0
  67. package/tests/common/setup.js +45 -0
  68. package/tests/complete.test.js +27 -0
  69. package/tests/extensions/toMatchSchema.js +20 -0
  70. package/tests/history.test.js +86 -0
  71. package/tests/stream.test.js +67 -0
  72. package/tsconfig.json +12 -0
@@ -0,0 +1,69 @@
1
+ const matchReply = () => ({
2
+ message: expect.stringMatching(/.+/),
3
+ result: expect.arrayContaining([
4
+ expect.objectContaining({
5
+ role: expect.stringMatching(/^(assistant)$/),
6
+ content: expect.stringMatching(/.+/)
7
+ })
8
+ ]),
9
+ tokens: expect.objectContaining({
10
+ input: expect.toBeGreaterThan(0),
11
+ output: expect.toBeGreaterThan(0),
12
+ total: expect.toBeGreaterThan(0)
13
+ }),
14
+ uuid: expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i),
15
+ // New validation for JSON structure
16
+ message: expect.stringMatching(/.+/)
17
+ });
18
+
19
+ const matchToolReply = (name) => ({
20
+ message: expect.anything(),
21
+ result: expect.arrayContaining([
22
+ expect.objectContaining({
23
+ role: expect.stringMatching(/^(assistant)$/),
24
+ tool_calls: expect.arrayContaining([
25
+ expect.objectContaining({
26
+ id: expect.stringMatching(/.+/),
27
+ function: expect.objectContaining({
28
+ arguments: expect.stringMatching(/.+/),
29
+ name: expect.stringMatching(new RegExp(`^${name}$`))
30
+ }),
31
+ })
32
+ ])
33
+ }),
34
+ expect.objectContaining({
35
+ role: expect.stringMatching(/tool/),
36
+ name: expect.stringMatching(new RegExp(`^${name}$`)),
37
+ content: expect.stringMatching(/.+/),
38
+ tool_call_id: expect.stringMatching(/.+/)
39
+ }),
40
+ expect.objectContaining({
41
+ role: expect.stringMatching(/assistant/),
42
+ content: expect.stringMatching(/.+/)
43
+ })
44
+ ]),
45
+ tokens: expect.objectContaining({
46
+ input: expect.toBeGreaterThan(0),
47
+ output: expect.toBeGreaterThan(0),
48
+ total: expect.toBeGreaterThan(0)
49
+ }),
50
+ uuid: expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
51
+ });
52
+
53
+ const matchSchemaReply = (schema) => ({
54
+ message: expect.toMatchSchema(schema),
55
+ result: expect.arrayContaining([
56
+ expect.objectContaining({
57
+ role: expect.stringMatching(/^(assistant)$/),
58
+ content: expect.stringMatching(/.+/)
59
+ })
60
+ ]),
61
+ tokens: expect.objectContaining({
62
+ input: expect.toBeGreaterThan(0),
63
+ output: expect.toBeGreaterThan(0),
64
+ total: expect.toBeGreaterThan(0)
65
+ }),
66
+ uuid: expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i),
67
+ });
68
+
69
+ module.exports = { matchReply, matchToolReply, matchSchemaReply };
@@ -0,0 +1,45 @@
1
+ const Llaminate = require("../../dist/llaminate.min.js").Llaminate;
2
+ require('../extensions/toMatchSchema.js');
3
+
4
+ const llaminate = new Llaminate({
5
+ endpoint: process.env.TEST_ENDPOINT,
6
+ key: process.env.TEST_API_KEY,
7
+ model: process.env.TEST_MODEL,
8
+ system: ["You are a sarcastic assistant who answers very briefly and bluntly."],
9
+ rpm: Number(process.env.TEST_RPM)
10
+ });
11
+
12
+ const tools = [
13
+ {
14
+ function: {
15
+ name: "get_current_time",
16
+ description: "Returns the current time in ISO format.",
17
+ parameters: {
18
+ type: "object",
19
+ properties: {},
20
+ required: [],
21
+ }
22
+ },
23
+ handler: async () => {
24
+ return new Date().toISOString();
25
+ }
26
+ }
27
+ ];
28
+
29
+ const schema = {
30
+ type: "object",
31
+ properties: {
32
+ reply: {
33
+ type: "string",
34
+ description: "Your response to the user's query."
35
+ },
36
+ thoughts: {
37
+ type: "string",
38
+ description: "Your internal thoughts about the user's query."
39
+ },
40
+ },
41
+ required: ["reply", "thoughts"],
42
+ additionalProperties: false,
43
+ };
44
+
45
+ module.exports = { Llaminate, llaminate, tools, schema };
@@ -0,0 +1,27 @@
1
+ const { Llaminate, llaminate, config, tools, schema } = require("./common/setup.js");
2
+ const { matchReply, matchToolReply, matchSchemaReply } = require("./common/matches.js");
3
+
4
+ describe("Completion", () => {
5
+
6
+ beforeEach(() => { llaminate.clear(); });
7
+ afterAll(() => { llaminate.clear(); });
8
+
9
+ test("given a question, replies with an answer", async () => {
10
+ await expect(llaminate.complete("What's the capital of France?"))
11
+ .resolves.toMatchObject(matchReply());
12
+ });
13
+
14
+ test("given a tool and a relevant question, replies using the tool", async () => {
15
+ await expect(llaminate.complete("What time is it?", { tools } ))
16
+ .resolves.toMatchObject(matchToolReply(tools[0].function.name));
17
+ });
18
+
19
+ test("given a schema, replies using that schema", async () => {
20
+ await expect(llaminate.complete("How many bicycles are there in Beijing?", { schema }))
21
+ .resolves.toMatchObject(matchSchemaReply(schema));
22
+ });
23
+
24
+ beforeAll(() => { llaminate.clear(); });
25
+ afterAll(() => { llaminate.clear(); });
26
+
27
+ });
@@ -0,0 +1,20 @@
1
+ const Ajv = require("ajv");
2
+
3
+ function toMatchSchema(obj, schema) {
4
+ const ajv = new Ajv();
5
+ const validate = ajv.compile(schema);
6
+
7
+ return {
8
+ message: () => `expected ${this.utils.printReceived(json)} to match schema ${this.utils.printExpected(schema)}`,
9
+ pass: validate(obj),
10
+ };
11
+ }
12
+
13
+ function toBeGreaterThan(received, floor) {
14
+ return {
15
+ message: () => `expected ${this.utils.printReceived(received)} to be greater than ${floor}`,
16
+ pass: received > floor,
17
+ };
18
+ }
19
+
20
+ expect.extend({ toMatchSchema, toBeGreaterThan });
@@ -0,0 +1,86 @@
1
+ const { Llaminate, llaminate, config, tools, schema } = require("./common/setup.js");
2
+
3
+ const system = ["Reply in a traditional haiku format."];
4
+
5
+ const messages = [
6
+ { role: "user", content: "If clouds were made of candy, what flavor would they be?" },
7
+ { role: "assistant", content: "Cotton candy. Obviously." },
8
+ { role: "user", content: "What about rain?" },
9
+ { role: "assistant", content: "Lemon. Duh." },
10
+ { role: "user", content: "And snowflakes?" },
11
+ { role: "assistant", content: "Vanilla. What else?" },
12
+ { role: "user", content: "What if the moon was made of cheese?" },
13
+ { role: "assistant", content: "Cheddar. Maybe brie. Happy?" },
14
+ { role: "user", content: "Would stars be edible too?" },
15
+ { role: "assistant", content: "Popping candy. Next." },
16
+ { role: "user", content: "What would the sun taste like?" },
17
+ { role: "assistant", content: "Cinnamon. Spicy." },
18
+ { role: "user", content: "If the ocean was a drink, what would it be?" },
19
+ { role: "assistant", content: "Blue slushie. Salty." },
20
+ { role: "user", content: "What about the wind?" },
21
+ { role: "assistant", content: "Mint. Refreshing." },
22
+ { role: "user", content: "If mountains were desserts, what would they be?" },
23
+ { role: "assistant", content: "Chocolate cake. Tall." },
24
+ { role: "user", content: "What about the stars again?" }
25
+ ];
26
+
27
+ describe("History", () => {
28
+
29
+ beforeAll(() => { llaminate.clear(); });
30
+ afterAll(() => { llaminate.clear(); });
31
+
32
+ test("given an initial state, the history is correct", async () => {
33
+ const history = await llaminate.export();
34
+ await expect(history.length).toBe(1);
35
+ await expect(history[0]).toMatchObject({
36
+ role: "system",
37
+ content: llaminate.config.system[0]
38
+ });
39
+ });
40
+
41
+ test("given a prompt, the history expands", async () => {
42
+ const message = "What does the fox say?";
43
+ await llaminate.complete(message);
44
+ const history = await llaminate.export();
45
+ await expect(history.length).toBe(3);
46
+ await expect(history[0]).toMatchObject({
47
+ role: "system",
48
+ content: llaminate.config.system[0]
49
+ });
50
+ await expect(history[1]).toMatchObject({
51
+ role: "user",
52
+ content: message
53
+ });
54
+ await expect(history[2]).toMatchObject({
55
+ role: "assistant",
56
+ content: expect.stringMatching(/.+/)
57
+ });
58
+ });
59
+
60
+ test("given a series of messages, the history is correct", async () => {
61
+ await llaminate.complete(messages, { system });
62
+
63
+ const history = await llaminate.export();
64
+ await expect(history.length).toBe(messages.length + 2); // + 2 for system message and assistant's final reply
65
+
66
+ await expect(history[0]).toMatchObject({ role: "system", content: llaminate.config.system[0] });
67
+ for (let i = 1; i < messages.length; i++) {
68
+ await expect(history[i + 1]).toMatchObject(messages[i]);
69
+ }
70
+ await expect(history[history.length - 1]).toMatchObject({
71
+ role: "assistant",
72
+ content: expect.stringMatching(/.+/)
73
+ });
74
+ });
75
+
76
+ test("when history is cleared, the history is correct", async () => {
77
+ await llaminate.clear();
78
+ const history = await llaminate.export();
79
+ await expect(history.length).toBe(1);
80
+ await expect(history[0]).toMatchObject({
81
+ role: "system",
82
+ content: llaminate.config.system[0]
83
+ });
84
+ });
85
+
86
+ });
@@ -0,0 +1,67 @@
1
+ const { Llaminate, llaminate, config, tools, schema } = require("./common/setup.js");
2
+ const { matchReply, matchToolReply, matchSchemaReply } = require("./common/matches.js");
3
+
4
+ describe("Streaming", () => {
5
+ beforeEach(() => { llaminate.clear(); });
6
+ afterAll(() => { llaminate.clear(); });
7
+
8
+ test("given a question, replies with a stream", async () => {
9
+ const stream = await llaminate.stream("What's the capital of France?");
10
+
11
+ const chunks = [];
12
+ for await (const chunk of stream) chunks.push(chunk);
13
+
14
+ for (let i = 0; i < chunks.length; i++) {
15
+ expect(chunks[i]).toMatchObject({
16
+ message: expect.anything(String),
17
+ uuid: expect.stringMatching(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i)
18
+ });
19
+
20
+ if (i > 0) {
21
+ // Each chunk's message should contain the previous chunk's message
22
+ expect(chunks[i].message).toContain(chunks[i - 1].message);
23
+ // All chunks should have the same UUID
24
+ expect(chunks[i].uuid).toBe(chunks[0].uuid);
25
+ }
26
+
27
+ if (i < chunks.length - 1) {
28
+ expect(chunks[i].result).toBeNull();
29
+ expect(chunks[i].tokens).toBeNull();
30
+ } else {
31
+ expect(chunks[i]).toMatchObject(matchReply());
32
+ }
33
+ }
34
+ });
35
+
36
+ test("given a tool and a relevant question, replies with a stream and uses the tool", async () => {
37
+ const stream = await llaminate.stream("What time is it?", { tools });
38
+
39
+ const chunks = [];
40
+ for await (const chunk of stream) chunks.push(chunk);
41
+
42
+ expect(chunks.length).toBeGreaterThan(1);
43
+ expect(chunks[chunks.length - 1]).toMatchObject(matchToolReply(tools[0].function.name));
44
+ });
45
+
46
+ test("given a schema, replies with a stream and uses that schema", async () => {
47
+ llaminate.clear();
48
+ const stream = await llaminate.stream("How many bicycles are there in Beijing?", { schema });
49
+
50
+ const chunks = [];
51
+ for await (const chunk of stream) chunks.push(chunk);
52
+
53
+ expect(chunks.length).toBeGreaterThan(1);
54
+ expect(chunks[chunks.length - 1]).toMatchObject(matchSchemaReply(schema));
55
+ });
56
+
57
+ test("given a schema, a tool and a relevant question, uses the tool and replies with a stream adhering to the schema", async () => {
58
+ const stream = await llaminate.stream("What time is it?", { schema, tools });
59
+
60
+ const chunks = [];
61
+ for await (const chunk of stream) chunks.push(chunk);
62
+
63
+ expect(chunks.length).toBeGreaterThan(1);
64
+ expect(chunks[chunks.length - 1]).toMatchObject(matchToolReply(tools[0].function.name));
65
+ });
66
+
67
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "sourceMap": true,
4
+ "outDir": "dist",
5
+ "target": "es2018",
6
+ "module": "CommonJS",
7
+ "declaration": true,
8
+ "strict": false
9
+ },
10
+ "include": ["src/**/*"],
11
+ "exclude": ["node_modules"]
12
+ }