Skip to content

Commit 55256cd

Browse files
authored
Merge pull request #351 from lyjeileen/main
fix: sent chatCompletion message correctly
2 parents 8722347 + 9b372cf commit 55256cd

File tree

8 files changed

+208
-137
lines changed

8 files changed

+208
-137
lines changed

src/components/input/baseInput/baseInput.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import { type ForwardedRef, forwardRef, useRef, useState } from 'react'
2121
import React from 'react'
2222
import { v4 as getUUID } from 'uuid'
2323

24-
import { toChatRequest } from '../../helper'
2524
import Icon from '../../icon/icon'
2625
import type { BaseInputProps, Message, Participant } from '../../types'
2726
import Emoji from '../emoji/emoji'
@@ -281,7 +280,7 @@ function BaseInputElement(
281280
sender: props.sender,
282281
conversationId: props.conversationId,
283282
format: 'chatCompletionRequest',
284-
data: toChatRequest(messageText),
283+
data: { text: messageText },
285284
}
286285

287286
props.send(formattedMessage)

src/components/input/multimodal/multimodalInput/multimodalInput.cy.tsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ describe('Input', () => {
2626
const videoFile = 'public/videoExamples/videoCaptions.mp4'
2727

2828
beforeEach(() => {
29+
const sendStub = cy.stub().as('sendMessage')
2930
const mockWsClient = {
30-
send: cy.stub(),
31+
send: sendStub,
3132
close: cy.stub(),
3233
reconnect: cy.stub(),
3334
}
@@ -135,6 +136,63 @@ describe('Input', () => {
135136
.should('equal', '')
136137
})
137138

139+
it(`should send message with correct content on ${viewport} screen`, () => {
140+
cy.viewport(viewport)
141+
142+
cy.intercept(
143+
{
144+
method: 'POST',
145+
url: '/upload?message-id=*',
146+
},
147+
{
148+
statusCode: 200,
149+
body: {
150+
url: 'http://test-url.com/image-component-example.png',
151+
name: 'image-component-example.png',
152+
},
153+
}
154+
).as('upload')
155+
cy.get('input[type=file]').selectFile([imageFile], {
156+
force: true,
157+
})
158+
cy.wait('@upload')
159+
const testMessage = 'Hello, World!'
160+
cy.get(textField).type(testMessage)
161+
cy.get(sendButton).click()
162+
cy.get('@sendMessage').then((stub) => {
163+
const sendStub = stub as unknown as sinon.SinonStub
164+
const sentMessage = sendStub.args[0][0]
165+
expect(sentMessage).to.deep.include({
166+
sender: testUser,
167+
conversationId: '1',
168+
format: 'chatCompletionRequest',
169+
data: {
170+
messages: [
171+
{
172+
content: [
173+
{
174+
type: 'text',
175+
text: testMessage,
176+
},
177+
{
178+
type: 'file_url',
179+
file_url: {
180+
url: 'http://test-url.com/image-component-example.png',
181+
name: 'image-component-example.png',
182+
},
183+
},
184+
],
185+
role: 'user',
186+
},
187+
],
188+
},
189+
})
190+
191+
expect(sentMessage.id).to.be.a('string')
192+
expect(sentMessage.timestamp).to.be.a('string')
193+
})
194+
})
195+
138196
it(`can add and delete files on ${viewport} screen`, () => {
139197
cy.viewport(viewport)
140198
cy.intercept(

src/components/input/multimodal/multimodalInput/multimodalInput.stories.tsx

Lines changed: 14 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ import axios from 'axios'
33
import React from 'react'
44
import { v4 as getUUID } from 'uuid'
55

6-
import {
7-
conversationIdDescription,
8-
getMembersDescription,
9-
} from '../../../sharedDescription'
10-
import type { FileData, Message } from '../../../types'
6+
import { textInputDescription } from '../../../sharedDescription'
7+
import type { Message } from '../../../types'
118
import meta from '../../textInput/textInput.stories'
129
import MultimodalInput from './multimodalInput'
1310

@@ -85,7 +82,7 @@ const multiModalInputMeta: Meta<React.ComponentProps<typeof MultimodalInput>> =
8582

8683
multiModalInputMeta.argTypes = {
8784
...meta.argTypes,
88-
conversationId: conversationIdDescription,
85+
...textInputDescription,
8986
uploadFileEndpoint: {
9087
description:
9188
'The API endpoint for sending a POST multipart-form request. If the JSON response includes a `fileId` property, it can be used to delete the file later. Path placeholders like `fileName` and `messageId`, will be automatically replaced with the actual file name and message ID.',
@@ -107,47 +104,6 @@ multiModalInputMeta.argTypes = {
107104
type: { summary: 'string' },
108105
},
109106
},
110-
label: {
111-
description:
112-
'Optional label text to be displayed in the input, which will then move to the top when the input is focused on. If both label and placeholder are provided, the placeholder will only be visible once the input is focused on.',
113-
table: {
114-
type: { summary: 'string' },
115-
},
116-
},
117-
placeholder: {
118-
description:
119-
'Optional Placeholder text to be displayed in the input before user starts typing.',
120-
table: {
121-
type: { summary: 'string' },
122-
},
123-
},
124-
multiline: {
125-
description:
126-
'Optional boolean that dictates whether `TextInput` can expand to be multiline.',
127-
table: {
128-
type: { summary: 'boolean' },
129-
},
130-
},
131-
maxRows: {
132-
description: 'Optional maximum number of rows to be displayed.',
133-
table: {
134-
type: { summary: 'number' },
135-
},
136-
},
137-
fullWidth: {
138-
description:
139-
'Optional boolean that dictates whether `TextInput` takes up 100% width of the parent container.',
140-
table: {
141-
type: { summary: 'boolean' },
142-
},
143-
},
144-
enableSpeechToText: {
145-
description:
146-
'Optional boolean to enable speech-to-text. See which browsers are supported [here](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition#browser_compatibility).',
147-
table: {
148-
type: { summary: 'boolean' },
149-
},
150-
},
151107
maxFileCount: {
152108
description:
153109
'Optional props. Maximum number of files that can be uploaded in one message.',
@@ -212,15 +168,6 @@ multiModalInputMeta.argTypes = {
212168
},
213169
},
214170
},
215-
maximumEmojiSearchResults: {
216-
description:
217-
'Specifies the maximum number of emoji search results to display when the user enters a search query. The search query is triggered when the user types in a format like `:text`.',
218-
table: {
219-
type: { summary: 'number' },
220-
defaultValue: { summary: '5' },
221-
},
222-
},
223-
getMembers: getMembersDescription,
224171
}
225172

226173
export default multiModalInputMeta
@@ -234,24 +181,17 @@ export const Default = {
234181
placeholder: 'Type your message',
235182
ws: {
236183
send: (message: Message) => {
237-
let fileMessage = ''
238-
let textMessage = ''
239-
if (message.data.files && message.data.files.length > 0) {
240-
const fileNames = message.data.files
241-
.map((file: FileData) => file.name)
242-
.join(', ')
243-
fileMessage = `File(s): ${fileNames}`
244-
}
245-
if (message.data.text) {
246-
textMessage = `Text content: ${message.data.text}`
247-
}
248-
alert(
249-
'Message sent!' +
250-
'\n' +
251-
textMessage +
252-
`${textMessage.length > 0 ? '\n' : ''}` +
253-
fileMessage
254-
)
184+
const messages: string[] = []
185+
186+
message.data.messages[0].content.forEach((content: any) => {
187+
if (content.type === 'text') {
188+
messages.push(`Text content: ${content.text}`)
189+
} else if (content.type === 'file_url') {
190+
messages.push(`File: ${content.file_url.name}`)
191+
}
192+
})
193+
194+
alert('Message sent!\n' + messages.join('\n'))
255195
},
256196
},
257197
listFiles: () => {

src/components/input/multimodal/multimodalInput/multimodalInput.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,13 @@ export default function MultimodalInput({
107107
})
108108

109109
function handleSendMessage(formattedMessage: Message): void {
110-
if (isUploadFinished) {
111-
formattedMessage.id = messageId
112-
formattedMessage.format = 'chatCompletionRequest'
113-
formattedMessage.data = toChatRequest(
114-
formattedMessage.data.text,
115-
filesInfo.uploaded.map((file) => {
116-
return { url: file.url, name: file.name }
117-
})
118-
)
119-
}
110+
formattedMessage.data = toChatRequest(
111+
formattedMessage.data.text,
112+
filesInfo.uploaded.map((file) => {
113+
return { url: file.url, name: file.name }
114+
})
115+
)
116+
formattedMessage.id = messageId
120117

121118
props.ws.send(formattedMessage)
122119
setMessageId(getUUID())

src/components/input/textInput/textInput.cy.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ describe('TextInput', () => {
1818
const emojiTestWaitTime = 20
1919
context('Regular', () => {
2020
beforeEach(() => {
21+
const sendStub = cy.stub().as('sendMessage')
2122
const mockWsClient = {
22-
send: cy.stub(),
23+
send: sendStub,
2324
close: cy.stub(),
2425
reconnect: cy.stub(),
2526
}
@@ -118,6 +119,39 @@ describe('TextInput', () => {
118119
cy.get(textInput).type(':polpo:')
119120
cy.get('textarea').invoke('val').should('equal', '🐙')
120121
})
122+
it(`should send message with correct content on ${viewport} screen`, () => {
123+
cy.viewport(viewport)
124+
125+
const testMessage = 'Hello, World!'
126+
cy.get(textInput).type(testMessage)
127+
cy.get(sendButton).click()
128+
129+
cy.get('@sendMessage').then((stub) => {
130+
const sendStub = stub as unknown as sinon.SinonStub
131+
const sentMessage = sendStub.args[0][0]
132+
expect(sentMessage).to.deep.include({
133+
sender: testUser,
134+
conversationId: '1',
135+
format: 'chatCompletionRequest',
136+
data: {
137+
messages: [
138+
{
139+
content: [
140+
{
141+
type: 'text',
142+
text: testMessage,
143+
},
144+
],
145+
role: 'user',
146+
},
147+
],
148+
},
149+
})
150+
151+
expect(sentMessage.id).to.be.a('string')
152+
expect(sentMessage.timestamp).to.be.a('string')
153+
})
154+
})
121155
})
122156
})
123157

src/components/input/textInput/textInput.stories.tsx

Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import type { Meta, StoryFn } from '@storybook/react'
22
import React from 'react'
33

4-
import { getMembersDescription, wsDescription } from '../../sharedDescription'
4+
import { textInputDescription } from '../../sharedDescription'
55
import type { BaseInputProps, WebSocketClient } from '../../types'
66
import type { Message } from '../../types'
7-
import BaseInput from '../baseInput/baseInput'
87
import TextInput from './textInput'
98

109
interface InputProps extends BaseInputProps {
@@ -13,29 +12,10 @@ interface InputProps extends BaseInputProps {
1312

1413
const meta: Meta<React.FC<InputProps>> = {
1514
title: 'Rustic UI/Input/Text Input',
16-
component: BaseInput,
15+
component: TextInput,
1716
tags: ['autodocs'],
1817
parameters: {
1918
layout: 'centered',
20-
docs: {
21-
argTypes: {
22-
exclude: ['send', 'isSendEnabled'],
23-
},
24-
source: {
25-
transform: (code: string) => {
26-
let textInputCode = code.replaceAll('BaseInput', 'TextInput')
27-
textInputCode = textInputCode.replaceAll(
28-
' component={() => {}}\n',
29-
''
30-
)
31-
textInputCode = textInputCode.replaceAll(
32-
'send={() => {}}',
33-
'ws:{send: (message: Message) => alert(`Message sent: ${message.data.messages[0].content[0].text}`)}'
34-
)
35-
return textInputCode
36-
},
37-
},
38-
},
3919
},
4020
decorators: [
4121
(Story: StoryFn) => (
@@ -48,31 +28,7 @@ const meta: Meta<React.FC<InputProps>> = {
4828

4929
meta.argTypes = {
5030
...meta.argTypes,
51-
ws: wsDescription,
52-
getMembers: getMembersDescription,
53-
emojiDataSource: {
54-
description:
55-
'URL to fetch the emoji data from. You need to host the emoji data by yourself. If not provided, the default url will be used.',
56-
table: {
57-
type: { summary: 'string' },
58-
defaultValue: {
59-
summary:
60-
'https://cdn.jsdelivr.net/npm/emoji-picker-element-data@^1/en/emojibase/data.json',
61-
},
62-
},
63-
},
64-
sender: {
65-
description: 'The sender of the message.',
66-
type: { name: 'object', required: true, value: {} },
67-
table: {
68-
type: {
69-
summary: 'Sender',
70-
detail:
71-
'id: String representing sender id.\n' +
72-
'name: Optional string of sender name.',
73-
},
74-
},
75-
},
31+
...textInputDescription,
7632
}
7733

7834
export default meta
@@ -85,8 +41,15 @@ export const Default = {
8541
placeholder: 'Type your message',
8642
emojiDataSource:
8743
'node_modules/emoji-picker-element-data/en/emojibase/data.json',
88-
send: (message: Message) =>
89-
alert(`Message sent: ${message.data.messages[0].content[0].text}`),
44+
ws: {
45+
send: (message: Message) => {
46+
alert(
47+
'Message sent!' +
48+
'\n' +
49+
`Text content: ${message.data.messages[0].content[0].text}`
50+
)
51+
},
52+
},
9053
getMembers: () =>
9154
Promise.resolve([
9255
{

src/components/input/textInput/textInput.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React from 'react'
22

3+
import { toChatRequest } from '../../helper'
34
import type { Message, TextInputProps } from '../../types'
45
import BaseInput from '../baseInput/baseInput'
56

67
export default function TextInput(props: TextInputProps) {
78
const { ws, ...baseInputProps } = props
89

910
function handleSendMessage(message: Message): void {
11+
message.data = toChatRequest(message.data.text)
1012
ws.send(message)
1113
}
1214

0 commit comments

Comments
 (0)