with every commit, Miku grows stronger.
changed some defaults; added and then decided to drop repetition penalty related hyperparameters; fixed prompt formatting
This commit is contained in:
@@ -7,19 +7,65 @@ import { logError, logInfo } from '../../logging';
|
||||
import { LLMConfig } from '../commands/types';
|
||||
|
||||
|
||||
const USER_PROMPT = `Continue the following Discord conversation by completing the next message, playing the role of Hatsune Miku. Each message is represented as a line of JSON. Maintain the same JSON format as the preceding messages. Refer to other users by their "name" instead of "author" field whenever possible.
|
||||
const RESPONSE_REGEX = `\\{"timestamp":"(Sun|Mon|Tue|Wed|Thu|Fri|Sat), \\d{2} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \\d{4} \\d{2}:\\d{2}:\\d{2} GMT","author":"Hatsune Miku#1740","name":"Hatsune Miku","context":"([^"\\\\]|\\\\.)*","content":"([^"\\\\]|\\\\.)*"(,"reactions":("(:\\w+: \\(\\d+\\)(, )?)*"|null))?\\}`;
|
||||
|
||||
The output should be formatted as a JSON instance that conforms to the JSON schema below.
|
||||
const RESPONSE_SCHEMA = {
|
||||
"properties": {
|
||||
"timestamp": {
|
||||
"description": "When the message was sent, in RFC 7231 format",
|
||||
"title": "Timestamp",
|
||||
"type": "string"
|
||||
},
|
||||
"author": {
|
||||
"description": "The author's username, which may be one of the following, or something else: \"vinso1445\", \"f0oby\", \"1thinker\", \"scoliono\", \"ahjc\", \"cinnaba\", \"M6481\", \"hypadrive\", \"need_correction\", \"Hatsune Miku#1740\" (You)",
|
||||
"title": "Author",
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"description": "The author's real name, which may be blank or one of the following: \"Vincent Iannelli\", \"Myles Linden\", \"Samuel Habib\", \"James Shiffer\", \"Alex\", \"Jinsung Park\", \"Lawrence Liu\", \"Nazar Khan\", \"Ethan Cheng\", \"Hatsune Miku\" (You)",
|
||||
"title": "Name"
|
||||
},
|
||||
"context": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"default": null,
|
||||
"description": "The contents of the message being replied to, if this message is a reply",
|
||||
"title": "Context"
|
||||
},
|
||||
"content": {
|
||||
"description": "The text content of this message",
|
||||
"title": "Content",
|
||||
"type": "string"
|
||||
},
|
||||
"reactions": {
|
||||
"anyOf": [
|
||||
{"type": "string"},
|
||||
{"type": "null"}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Optional list of emoji reactions this message received, if any. The following comma-separated format is used: \":skull: (3), :100: (1)\"",
|
||||
"title": "Reactions"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"timestamp",
|
||||
"author",
|
||||
"name",
|
||||
"content"
|
||||
]
|
||||
};
|
||||
|
||||
As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
|
||||
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.
|
||||
const USER_PROMPT = `Continue the following Discord conversation by completing the next message, playing the role of Hatsune Miku. The conversation must progress forward, and you must avoid repeating yourself.
|
||||
|
||||
Here is the output schema:
|
||||
\`\`\`
|
||||
{"properties": {"timestamp": {"description": "When the message was sent, in RFC 7231 format", "title": "Timestamp", "type": "string"}, "author": {"description": "The author's username, which may be one of the following, or something else: \"vinso1445\", \"f0oby\", \"1thinker\", \"scoliono\", \"ahjc\", \"cinnaba\", \"M6481\", \"hypadrive\", \"need_correction\", \"Hatsune Miku#1740\" (You)", "title": "Author", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "description": "The author's real name, which may be blank or one of the following: \"Vincent Iannelli\", \"Myles Linden\", \"Samuel Habib\", \"James Shiffer\", \"Alex\", \"Jinsung Park\", \"Lawrence Liu\", \"Nazar Khan\", \"Ethan Cheng\", \"Hatsune Miku\" (You)", "title": "Name"}, "context": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "The contents of the message being replied to, if this message is a reply", "title": "Context"}, "content": {"description": "The text content of this message", "title": "Content", "type": "string"}, "reactions": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "description": "Optional list of emoji reactions this message received, if any. The following comma-separated format is used: \":skull: (3), :100: (1)\"", "title": "Reactions"}}, "required": ["timestamp", "author", "name", "content"]}
|
||||
\`\`\`
|
||||
Each message is represented as a line of JSON. Refer to other users by their "name" instead of their "author" field whenever possible.
|
||||
|
||||
The conversation is as follows. The last line is the message you have to complete:
|
||||
The conversation is as follows. The last line is the message you have to complete. Please ONLY return the string contents of the "content" field, that go in place of the ellipses. Do not include the enclosing quotation marks in your response.
|
||||
|
||||
`;
|
||||
|
||||
@@ -62,16 +108,14 @@ export class HuggingfaceProvider implements LLMProvider
|
||||
|
||||
let templateMsgTxt = JSON.stringify({
|
||||
timestamp: newDate.toUTCString(),
|
||||
author: lastMsg!.author,
|
||||
name: lastMsg!.name,
|
||||
author: "Hatsune Miku",
|
||||
name: "Hatsune Miku",
|
||||
context: lastMsg!.content,
|
||||
content: ""
|
||||
content: "..."
|
||||
});
|
||||
|
||||
// cut off the end, leaving an open string for the model to complete
|
||||
templateMsgTxt = templateMsgTxt.slice(0, templateMsgTxt.length - 2);
|
||||
|
||||
const messageHistoryTxt = messageList.map(msg => JSON.stringify(msg)).join('\n') + templateMsgTxt;
|
||||
const messageHistoryTxt = messageList.map(msg => JSON.stringify(msg)).join('\n') + '\n' + templateMsgTxt;
|
||||
logInfo(`[hf] Requesting response for message history: ${messageHistoryTxt}`);
|
||||
|
||||
try {
|
||||
const chatCompletion = await this.client.chatCompletion({
|
||||
@@ -80,38 +124,23 @@ export class HuggingfaceProvider implements LLMProvider
|
||||
{ role: "system", content: sysprompt },
|
||||
{ role: "user", content: USER_PROMPT + messageHistoryTxt }
|
||||
],
|
||||
temperature: params?.temperature || 0.9,
|
||||
max_tokens: params?.max_new_tokens || 256,
|
||||
top_p: params?.top_p || 0.5
|
||||
temperature: params?.temperature || 0.5,
|
||||
top_p: params?.top_p || 0.9,
|
||||
max_tokens: params?.max_new_tokens || 128,
|
||||
/*response_format: {
|
||||
type: "regex",
|
||||
value: String(RESPONSE_REGEX)
|
||||
}*/
|
||||
});
|
||||
|
||||
let raw = chatCompletion.choices[0].message.content;
|
||||
logInfo(`[hf] API response: ${raw}`);
|
||||
let response = chatCompletion.choices[0].message.content;
|
||||
logInfo(`[hf] API response: ${response}`);
|
||||
|
||||
if (!raw) {
|
||||
if (!response) {
|
||||
throw new TypeError("HuggingFace completion API returned no message.");
|
||||
}
|
||||
|
||||
let responseMessage;
|
||||
try {
|
||||
responseMessage = JSON.parse(raw);
|
||||
} catch (err) {
|
||||
// gotta make sure we properly terminate our json
|
||||
if (!raw.endsWith('"}')) {
|
||||
if (raw.endsWith('"')) {
|
||||
raw += '}';
|
||||
} else {
|
||||
raw += '"}';
|
||||
}
|
||||
}
|
||||
responseMessage = JSON.parse(raw);
|
||||
}
|
||||
|
||||
if (!responseMessage.content) {
|
||||
throw new TypeError("HuggingFace completion API returned a message with no content.");
|
||||
}
|
||||
|
||||
return responseMessage.content;
|
||||
return response;
|
||||
} catch (err) {
|
||||
logError(`[hf] API Error: ` + err);
|
||||
throw err;
|
||||
|
||||
Reference in New Issue
Block a user