Many-in-one AI client.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
7.1 KiB

  1. import { config } from 'dotenv';
  2. import {
  3. beforeAll,
  4. beforeEach,
  5. describe,
  6. expect,
  7. it,
  8. } from 'vitest';
  9. import {
  10. createAiClient,
  11. OpenAi,
  12. } from '../../../src';
  13. import { getPromptTokens } from '../../../src/platforms/openai';
  14. import { ChatCompletionModel } from '../../../src/platforms/openai';
  15. describe('OpenAI', () => {
  16. beforeAll(() => {
  17. config();
  18. });
  19. describe.skip('API', () => {
  20. let aiClient: OpenAi.PlatformEventEmitter;
  21. beforeEach(() => {
  22. aiClient = createAiClient({
  23. platform: OpenAi.PLATFORM_ID,
  24. platformConfiguration: {
  25. apiKey: process.env.OPENAI_API_KEY as string,
  26. organizationId: process.env.OPENAI_ORGANIZATION_ID as string,
  27. apiVersion: OpenAi.ApiVersion.V1,
  28. },
  29. });
  30. });
  31. describe('createChatCompletion', () => {
  32. let result: Partial<OpenAi.ChatCompletion> | undefined;
  33. let prompt: string;
  34. beforeEach(() => {
  35. result = undefined;
  36. aiClient.on<OpenAi.ChatCompletionChunkDataEvent>('data', (d) => {
  37. d.choices.forEach((c) => {
  38. if (!result) {
  39. const promptTokens = getPromptTokens(
  40. OpenAi.normalizeChatMessage(prompt),
  41. ChatCompletionModel.GPT_3_5_TURBO,
  42. )
  43. .length;
  44. result = {
  45. id: d.id,
  46. object: OpenAi.ChatCompletionDataEventObjectType.CHAT_COMPLETION,
  47. created: d.created,
  48. model: d.model,
  49. usage: {
  50. prompt_tokens: promptTokens,
  51. completion_tokens: 0,
  52. total_tokens: promptTokens,
  53. },
  54. };
  55. }
  56. if (!Array.isArray(result?.choices)) {
  57. result.choices = [];
  58. }
  59. if (!result.choices[c.index]) {
  60. result.choices[c.index] = {
  61. message: { content: '' },
  62. index: c.index,
  63. finish_reason: c.finish_reason,
  64. };
  65. }
  66. if (result.choices[c.index].message) {
  67. if (c.delta.role) {
  68. (result.choices[c.index].message as Record<string, unknown>).role = c.delta.role;
  69. }
  70. if (c.delta.content) {
  71. (result.choices[c.index].message as Record<string, unknown>)
  72. .content += c.delta.content;
  73. result.usage!.completion_tokens += 1;
  74. result.usage!.total_tokens = (
  75. result.usage!.prompt_tokens + result.usage!.completion_tokens
  76. );
  77. }
  78. }
  79. if (c.finish_reason) {
  80. result.choices[c.index].finish_reason = c.finish_reason;
  81. }
  82. });
  83. });
  84. });
  85. it('works', () => new Promise<void>((resolve, reject) => {
  86. aiClient.on('end', () => {
  87. expect(result).toHaveProperty('id', expect.any(String));
  88. expect(result).toHaveProperty('object', OpenAi.ChatCompletionDataEventObjectType.CHAT_COMPLETION);
  89. expect(result).toHaveProperty('model', expect.any(String));
  90. expect(result).toHaveProperty('created', expect.any(Number));
  91. expect(result).toHaveProperty('choices', expect.any(Array));
  92. resolve();
  93. });
  94. aiClient.on('error', (error: Error) => {
  95. reject(error);
  96. });
  97. prompt = 'Count from 1 to 20 in increments of a random number from 1 to 10.';
  98. aiClient.createChatCompletion({
  99. messages: prompt,
  100. model: OpenAi.ChatCompletionModel.GPT_3_5_TURBO,
  101. n: 2,
  102. });
  103. }), { timeout: 10000 });
  104. });
  105. describe('createImage', () => {
  106. it('works', () => new Promise<void>((resolve, reject) => {
  107. aiClient.on<OpenAi.ImageDataEvent>('data', (r) => {
  108. expect(r).toHaveProperty('created', expect.any(Number));
  109. expect(r).toHaveProperty('data', expect.any(Array));
  110. expect(r.data.every((d) => d instanceof Buffer)).toBe(true);
  111. });
  112. aiClient.on('end', () => {
  113. resolve();
  114. });
  115. aiClient.on('error', (error: Error) => {
  116. reject(error);
  117. });
  118. aiClient.createImage({
  119. prompt: 'A photo of a cat',
  120. size: OpenAi.ImageSize.SQUARE_256,
  121. });
  122. }), { timeout: 10000 });
  123. });
  124. describe('createCompletion', () => {
  125. let result: Partial<OpenAi.TextCompletion> | undefined;
  126. beforeEach(() => {
  127. result = undefined;
  128. aiClient.on<OpenAi.TextCompletionChunkDataEvent>('data', (d) => {
  129. d.choices.forEach((c) => {
  130. if (!result) {
  131. result = {
  132. id: d.id,
  133. object: OpenAi.TextCompletionDataEventObjectType.TEXT_COMPLETION,
  134. created: d.created,
  135. model: d.model,
  136. };
  137. }
  138. if (!Array.isArray(result?.choices)) {
  139. result.choices = [];
  140. }
  141. if (!result.choices[c.index]) {
  142. result.choices[c.index] = {
  143. text: '',
  144. index: c.index,
  145. finish_reason: c.finish_reason,
  146. logprobs: c.logprobs, // TODO dunno how to use this?
  147. };
  148. }
  149. if (c.text) {
  150. result.choices[c.index].text += c.text;
  151. }
  152. if (c.finish_reason) {
  153. result.choices[c.index].finish_reason = c.finish_reason;
  154. }
  155. });
  156. });
  157. });
  158. it('works', () => new Promise<void>((resolve, reject) => {
  159. aiClient.on('end', () => {
  160. expect(result).toHaveProperty('id', expect.any(String));
  161. expect(result).toHaveProperty('object', OpenAi.TextCompletionDataEventObjectType.TEXT_COMPLETION);
  162. expect(result).toHaveProperty('model', expect.any(String));
  163. expect(result).toHaveProperty('created', expect.any(Number));
  164. expect(result).toHaveProperty('choices', expect.any(Array));
  165. resolve();
  166. });
  167. aiClient.on('error', (error: Error) => {
  168. reject(error);
  169. });
  170. aiClient.createCompletion({
  171. prompt: 'Say this is a test',
  172. model: OpenAi.TextCompletionModel.TEXT_DAVINCI_003,
  173. maxTokens: 7,
  174. temperature: 0,
  175. });
  176. }), { timeout: 10000 });
  177. });
  178. describe('createEdit', () => {
  179. it('works', () => new Promise<void>((resolve, reject) => {
  180. aiClient.on<OpenAi.CreateEditDataEvent>('data', (r) => {
  181. expect(r).toHaveProperty('object', OpenAi.EditDataEventObjectType.EDIT);
  182. expect(r).toHaveProperty('created', expect.any(Number));
  183. expect(r).toHaveProperty('choices', expect.any(Array));
  184. });
  185. aiClient.on('end', () => {
  186. resolve();
  187. });
  188. aiClient.on('error', (error: Error) => {
  189. reject(error);
  190. });
  191. aiClient.createEdit({
  192. model: OpenAi.EditModel.TEXT_DAVINCI_EDIT_001,
  193. input: 'What day of the wek is it?',
  194. instruction: 'Fix the spelling mistakes',
  195. });
  196. }), { timeout: 10000 });
  197. });
  198. });
  199. });