1
0

chat.mjs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import * as readline from 'node:readline'
  2. import { stdin, stdout } from 'node:process'
  3. import { readFileSync } from 'node:fs'
  4. import { SchemaConverter } from './public/json-schema-to-grammar.mjs'
  5. const args = process.argv.slice(2);
  6. const grammarJsonSchemaFile = args.find(
  7. (_, index) => args[index - 1] === "--grammar-json-schema"
  8. );
  9. const grammarFile = args.find((_, index) => args[index - 1] === "--grammar");
  10. // Example usage: function,arguments
  11. const grammarJsonSchemaPropOrder = args.find(
  12. (_, index) => args[index - 1] === "--grammar-json-schema-prop-order"
  13. );
  14. const propOrder = grammarJsonSchemaPropOrder
  15. ? grammarJsonSchemaPropOrder
  16. .split(",")
  17. .reduce((acc, cur, index) => ({ ...acc, [cur]: index }), {})
  18. : {};
  19. let grammar = null
  20. if (grammarJsonSchemaFile) {
  21. const schema = JSON.parse(readFileSync(grammarJsonSchemaFile, 'utf-8'))
  22. const converter = new SchemaConverter(propOrder)
  23. converter.visit(schema, '')
  24. grammar = converter.formatGrammar()
  25. }
  26. if (grammarFile) {
  27. grammar = readFileSync(grammarFile, 'utf-8')
  28. }
  29. const API_URL = 'http://127.0.0.1:8080'
  30. const chat = [
  31. {
  32. human: "Hello, Assistant.",
  33. assistant: "Hello. How may I help you today?"
  34. },
  35. {
  36. human: "Please tell me the largest city in Europe.",
  37. assistant: "Sure. The largest city in Europe is Moscow, the capital of Russia."
  38. },
  39. ]
  40. const instruction = `A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.`
  41. function format_prompt(question) {
  42. return `${instruction}\n${
  43. chat.map(m =>`### Human: ${m.human}\n### Assistant: ${m.assistant}`).join("\n")
  44. }\n### Human: ${question}\n### Assistant:`
  45. }
  46. async function tokenize(content) {
  47. const result = await fetch(`${API_URL}/tokenize`, {
  48. method: 'POST',
  49. body: JSON.stringify({ content })
  50. })
  51. if (!result.ok) {
  52. return []
  53. }
  54. return await result.json().tokens
  55. }
  56. const n_keep = await tokenize(instruction).length
  57. async function chat_completion(question) {
  58. const result = await fetch(`${API_URL}/completion`, {
  59. method: 'POST',
  60. body: JSON.stringify({
  61. prompt: format_prompt(question),
  62. temperature: 0.2,
  63. top_k: 40,
  64. top_p: 0.9,
  65. n_keep: n_keep,
  66. n_predict: 256,
  67. stop: ["\n### Human:"], // stop completion after generating this
  68. grammar,
  69. stream: true,
  70. })
  71. })
  72. if (!result.ok) {
  73. return
  74. }
  75. let answer = ''
  76. for await (var chunk of result.body) {
  77. const t = Buffer.from(chunk).toString('utf8')
  78. if (t.startsWith('data: ')) {
  79. const message = JSON.parse(t.substring(6))
  80. answer += message.content
  81. process.stdout.write(message.content)
  82. if (message.stop) {
  83. if (message.truncated) {
  84. chat.shift()
  85. }
  86. break
  87. }
  88. }
  89. }
  90. process.stdout.write('\n')
  91. chat.push({ human: question, assistant: answer.trimStart() })
  92. }
  93. const rl = readline.createInterface({ input: stdin, output: stdout });
  94. const readlineQuestion = (rl, query, options) => new Promise((resolve, reject) => {
  95. rl.question(query, options, resolve)
  96. });
  97. while(true) {
  98. const question = await readlineQuestion(rl, '> ')
  99. await chat_completion(question)
  100. }