diff --git a/frontend/control-center/src/actions/streams/index.ts b/frontend/control-center/src/actions/streams/index.ts index 3d47b61e0..26619e2e6 100644 --- a/frontend/control-center/src/actions/streams/index.ts +++ b/frontend/control-center/src/actions/streams/index.ts @@ -10,6 +10,7 @@ const SET_TOPIC_INFO = '@@metadata/SET_TOPIC_INFO'; const SET_TOPIC_SCHEMAS = '@@metadata/SET_TOPIC_SCHEMAS'; const SET_STREAMS = '@@metadata/SET_STREAMS'; const SET_SCHEMAS_INFO = '@@metadata/SET_SCHEMAS_INFO'; +const SET_SCHEMAS_VERSIONS = '@@metadata/SET_SCHEMAS_VERSIONS'; const SET_STREAM_INFO = '@@metadata/SET_STREAM_INFO'; const SET_LAST_MESSAGE = '@@metadata/SET_LAST_MESSAGRE'; @@ -74,8 +75,23 @@ export const getSchemas = () => async (dispatch: Dispatch) => { }); }; -export const getSchemaInfo = (topicName: string) => async (dispatch: Dispatch) => { - return getData(`subjects/${topicName}/versions/latest`).then(response => { +export const getSchemaVersions = (topicName: string) => async (dispatch: Dispatch) => { + return getData(`subjects/${topicName}/versions`).then(response => { + if (response.error_code && response.error_code.toString().includes('404') && !topicName.includes('-value')) { + return Promise.reject('404 Not Found'); + } else { + dispatch(setCurrentSchemaVersionsAction({name: topicName, versions: response})); + } + return Promise.resolve(true); + }); +}; + +export const getSchemaInfo = (topicName: string, version?: string) => async (dispatch: Dispatch) => { + let v = 'latest'; + if (version) { + v = version; + } + return getData(`subjects/${topicName}/versions/${v}`).then(response => { if (response.error_code && response.error_code.toString().includes('404') && !topicName.includes('-value')) { return Promise.reject('404 Not Found'); } else { @@ -197,6 +213,11 @@ export const setStreamsAction = createAction(SET_STREAMS, (streams: Stream[]) => export const setCurrentSchemaInfoAction = createAction(SET_SCHEMAS_INFO, (topicInfo: Schema) => topicInfo)(); +export const setCurrentSchemaVersionsAction = createAction( + SET_SCHEMAS_VERSIONS, + (topicInfo: {name: string; versions: []}) => topicInfo +)<{name: string; versions: []}>(); + export const setCurrentStreamInfoAction = createAction( SET_STREAM_INFO, (streamInfo: StreamInfo) => streamInfo diff --git a/frontend/control-center/src/pages/LLMConsumers/index.module.scss b/frontend/control-center/src/pages/LLMConsumers/index.module.scss index a9f7469c3..8eb971f0d 100644 --- a/frontend/control-center/src/pages/LLMConsumers/index.module.scss +++ b/frontend/control-center/src/pages/LLMConsumers/index.module.scss @@ -126,3 +126,11 @@ margin-bottom: 12px; } } + +.dropdownContainer { + button { + border: 1px solid gray; + width: 100%; + color: black; + } +} diff --git a/frontend/control-center/src/pages/LLMConsumers/index.tsx b/frontend/control-center/src/pages/LLMConsumers/index.tsx index 9c43a3563..368267ee9 100644 --- a/frontend/control-center/src/pages/LLMConsumers/index.tsx +++ b/frontend/control-center/src/pages/LLMConsumers/index.tsx @@ -1,5 +1,5 @@ import React, {useEffect, useState} from 'react'; -import {Input, NotificationComponent} from 'components'; +import {Dropdown, Input, NotificationComponent} from 'components'; import {SettingsModal} from 'components/alerts/SettingsModal'; import {Button} from 'components/cta/Button'; import {useTranslation} from 'react-i18next'; @@ -7,18 +7,33 @@ import {connect, ConnectedProps} from 'react-redux'; import {setPageTitle} from '../../services/pageTitle'; import {NotificationModel} from 'model'; import {AiryLoader} from 'components/loaders/AiryLoader'; -import styles from './index.module.scss'; import {EmptyState} from './EmptyState'; import {HttpClientInstance} from '../../httpClient'; import {LLMConsumerItem} from './LLMConsumerItem'; +import {getValidTopics} from '../../selectors'; +import {StateModel} from '../../reducers'; +import styles from './index.module.scss'; +import {getSchemaInfo, getSchemas} from '../../actions'; type LLMConsumersProps = {} & ConnectedProps; -const mapDispatchToProps = {}; +const mapDispatchToProps = { + getSchemas, + getSchemaInfo, +}; + +const mapStateToProps = (state: StateModel) => { + return { + topics: getValidTopics(state), + schemas: state.data.streams.schemas, + }; +}; -const connector = connect(null, mapDispatchToProps); +const connector = connect(mapStateToProps, mapDispatchToProps); const LLMConsumers = (props: LLMConsumersProps) => { + const {topics, getSchemas} = props; + const [consumers, setConsumers] = useState([]); const [isLoading, setIsLoading] = useState(false); const [errorOccurred, setErrorOccurred] = useState(false); @@ -34,6 +49,7 @@ const LLMConsumers = (props: LLMConsumersProps) => { useEffect(() => { setPageTitle('LLM Consumers'); + getSchemas(); }, []); useEffect(() => { @@ -97,19 +113,19 @@ const LLMConsumers = (props: LLMConsumersProps) => { height={32} fontClass="font-base" /> - ) => setTopic(event.target.value)} - minLength={6} - required={true} - height={32} - fontClass="font-base" - /> +
+ { + setTopic(topic); + // getSchemaInfo(topic).catch(() => { + // getSchemaInfo(topic + '-value'); + // }); + }} + /> +
void; + setFirstTabSelected: (flag: boolean) => void; + editorMode: string; + wrapperSection: MutableRefObject; + isEditMode: boolean; + setIsEditMode: (flag: boolean) => void; + setErrorMessage: (error: string) => void; + setShowErrorPopUp: (flag: boolean) => void; + version: number; +} & ConnectedProps; + +const mapDispatchToProps = { + setSchemaSchema, + checkCompatibilityOfNewSchema, +}; + +const connector = connect(null, mapDispatchToProps); + +const EnrichedSchemaSection = (props: EnrichedSchemaSectionProps) => { + const { + schemaName, + code, + setCode, + setFirstTabSelected, + editorMode, + wrapperSection, + setSchemaSchema, + isEditMode, + setIsEditMode, + checkCompatibilityOfNewSchema, + setErrorMessage, + setShowErrorPopUp, + version, + } = props; + + const [localCode, setLocalCode] = useState(undefined); + const [hasBeenChanged, setHasBeenChanged] = useState(false); + const codeRef = useRef(null); + const {t} = useTranslation(); + + useEffect(() => { + if (isEnrichmentAvailable(code)) { + setTimeout(() => { + const enriched = localStorage.getItem(schemaName); + if (enriched) { + setLocalCode(enriched); + recalculateContainerHeight(enriched); + } + }, 100); + enrichCode(code); + } else { + wrapperSection.current.style.height = '156px'; + } + }, [code]); + + const resetCodeAndEndEdition = () => { + setIsEditMode(false); + setFirstTabSelected(true); + }; + + const recalculateContainerHeight = (code: string) => { + let basicHeight = 220; + if (wrapperSection && wrapperSection.current) { + wrapperSection.current.style.height = `${calculateHeightOfCodeString(code) + basicHeight}px`; + } else { + wrapperSection.current.style.height = `${basicHeight}px`; + } + if ((wrapperSection.current.style.height.replace('px', '') as number) > 700) { + wrapperSection.current.style.height = '700px'; + } + }; + + const recalculateCodeHeight = (code: string) => { + const codeHeight = calculateHeightOfCodeString(code); + if (codeHeight > 478) { + return 478; + } + return codeHeight; + }; + + const isEnrichmentAvailable = (code: string): boolean => { + let needsEnrichment = false; + const parsedCode = JSON.parse(code); + (parsedCode.fields || []).map(field => { + if (typeof field.type === 'object' && !Array.isArray(field.type)) { + if (!field.type.doc) { + needsEnrichment = true; + } + } else if (!field.doc) { + needsEnrichment = true; + } + }); + return needsEnrichment; + }; + + const enrichCode = async (code: string) => { + let enrichedSchema = localStorage.getItem(schemaName); + + if (!enrichedSchema) { + const enrichedCode = JSON.parse(code); + + // Use map to create an array of promises + const promises = (enrichedCode.fields || []).map(async field => { + console.log(typeof field.type); + if (typeof field.type === 'object' && !Array.isArray(field.type)) { + if (!field.type.doc) { + const doc = await generateDocForField(field); + field.type.doc = doc; + } + } else if (!field.doc) { + const doc = await generateDocForField(field); + field.doc = doc; + } + }); + + // Wait for all promises to resolve + await Promise.all(promises); + + enrichedSchema = JSON.stringify(enrichedCode, null, 2); + localStorage.setItem(schemaName, enrichedSchema); + } + + setLocalCode(enrichedSchema); + recalculateContainerHeight(enrichedSchema); + }; + + const saveEnrichedSchema = () => { + setSchemaSchema(schemaName, JSON.stringify(localCode, null, 2)); + }; + + const checkCompatibility = (_schemaName: string, _code: string, _version: number) => { + checkCompatibilityOfNewSchema(_schemaName, _code, _version) + .then(() => { + setSchemaSchema(_schemaName, _code) + .then(() => { + setCode(localCode); + setHasBeenChanged(false); + }) + .catch((e: string) => { + setIsEditMode(true); + setErrorMessage(e); + setShowErrorPopUp(true); + setTimeout(() => setShowErrorPopUp(false), 5000); + }); + }) + .catch((e: string) => { + if (e.includes('404')) { + checkCompatibility(_schemaName + '-value', _code, _version); + } else { + setIsEditMode(true); + setErrorMessage(e); + setShowErrorPopUp(true); + setTimeout(() => setShowErrorPopUp(false), 5000); + } + }); + }; + + const generateDocForField = async (field: any): Promise => { + try { + const response = await HttpClientInstance.llmQuery({ + query: `This is the payload of a metadata field of a Kafka Schema ${JSON.stringify( + field + )}. A the name of the schema is ${schemaName}. This is the whole schema: ${code}. Give an accurante description of the field, so the users can understand what it is and what it is used for.`, + }); + return response.answer.result; + } catch (error) { + console.error('Error in generateDocForField:', error); + return ''; + } + }; + + return ( + <> +
+
+ + +
+
+ {isEnrichmentAvailable(code) ? ( + <> +
+
+
+ This schema can be automatically enriched with documentation and saved as a new version as follows. +
+ +
+
+
New schema:
+
+ + {hasBeenChanged && ( + + )} +
+
+
+ {localCode && localCode !== '{}' && ( +
+ { + if (value !== code) { + setHasBeenChanged(true); + } else { + setHasBeenChanged(false); + } + }} + onBlur={() => { + setLocalCode(codeRef.current.editor.getModel().getValue()); + }} + options={{ + scrollBeyondLastLine: isEditMode, + readOnly: !isEditMode, + theme: editorMode, + }} + /> +
+ )} + + ) : ( +
+
+
This schema has been enriched already with documentation.
+
+
+ )} + + ); +}; + +export default connector(EnrichedSchemaSection); diff --git a/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaDescription.tsx b/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaDescription.tsx index 673d8dda0..36ff6cf9a 100644 --- a/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaDescription.tsx +++ b/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaDescription.tsx @@ -1,14 +1,14 @@ import React, {MutableRefObject, useEffect, useState} from 'react'; -import {getSchemaInfo} from '../../../../actions'; +import {getSchemaInfo, getSchemaVersions} from '../../../../actions'; import {connect, ConnectedProps} from 'react-redux'; import {ErrorPopUp} from 'components'; -import {calculateHeightOfCodeString} from '../../../../services'; import SchemaSection from './SchemaSection'; import styles from './index.module.scss'; -import {MessageSection, lastMessageMock} from './MessageSection'; +import EnrichedSchemaSection from './EnrichedSchemaSection'; const mapDispatchToProps = { getSchemaInfo, + getSchemaVersions, }; const connector = connect(null, mapDispatchToProps); @@ -19,15 +19,17 @@ type SchemaDescriptionProps = { setCode: (code: string) => void; wrapperSection: MutableRefObject; version: number; + versions: string[]; } & ConnectedProps; const SchemaDescription = (props: SchemaDescriptionProps) => { - const {schemaName, code, setCode, getSchemaInfo, wrapperSection, version} = props; + const {schemaName, code, setCode, getSchemaInfo, getSchemaVersions, wrapperSection, version, versions} = props; useEffect(() => { getSchemaInfo(schemaName).catch(() => { getSchemaInfo(schemaName + '-value'); }); + getSchemaVersions(schemaName); }, []); useEffect(() => { @@ -43,26 +45,14 @@ const SchemaDescription = (props: SchemaDescriptionProps) => { const [errorMessage, setErrorMessage] = useState(''); const [editorMode, setEditorMode] = useState(localStorage.getItem('theme') === 'dark' ? 'vs-dark' : 'vs'); - useEffect(() => { - if (firstTabSelected) { - recalculateContainerHeight(code); - } else { - recalculateContainerHeight(lastMessageMock); - } - }, [firstTabSelected, code]); - const setNewSchemaCode = (text: string) => { setCode(text); }; - const recalculateContainerHeight = (code: string) => { - const basicHeight = 50; - const headerHeight = 32; - if (wrapperSection && wrapperSection.current) { - wrapperSection.current.style.height = `${calculateHeightOfCodeString(code) + headerHeight + basicHeight}px`; - } else { - wrapperSection.current.style.height = `${basicHeight}px`; - } + const loadSchemaVersion = (version: string) => { + getSchemaInfo(schemaName, version).catch(() => { + getSchemaInfo(schemaName + '-value', version); + }); }; return ( @@ -76,17 +66,26 @@ const SchemaDescription = (props: SchemaDescriptionProps) => { setIsEditMode={setIsEditMode} setFirstTabSelected={setFirstTabSelected} editorMode={editorMode} - recalculateContainerHeight={recalculateContainerHeight} + wrapperSection={wrapperSection} setErrorMessage={setErrorMessage} setShowErrorPopUp={setShowErrorPopUp} version={version} + versions={versions} + loadSchemaVersion={loadSchemaVersion} /> ) : ( - )} {showErrorPopUp && setShowErrorPopUp(false)} />} diff --git a/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaSection/index.tsx b/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaSection/index.tsx index 701b6f136..aab370f65 100644 --- a/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaSection/index.tsx +++ b/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/SchemaSection/index.tsx @@ -1,11 +1,11 @@ -import React, {useEffect, useRef, useState} from 'react'; +import React, {MutableRefObject, useEffect, useRef, useState} from 'react'; import MonacoEditor from '@uiw/react-monacoeditor'; import {calculateHeightOfCodeString, isJSON} from '../../../../../services'; import {useTranslation} from 'react-i18next'; -import {Button} from 'components'; -import styles from '../index.module.scss'; +import {Button, Dropdown} from 'components'; import {checkCompatibilityOfNewSchema, setSchemaSchema} from '../../../../../actions'; import {ConnectedProps, connect} from 'react-redux'; +import styles from '../index.module.scss'; const mapDispatchToProps = { setSchemaSchema, @@ -22,10 +22,12 @@ type SchemaSectionProps = { setIsEditMode: (flag: boolean) => void; setFirstTabSelected: (flag: boolean) => void; editorMode: string; - recalculateContainerHeight: (text: string) => void; + wrapperSection: MutableRefObject; setErrorMessage: (error: string) => void; setShowErrorPopUp: (flag: boolean) => void; version: number; + loadSchemaVersion: (version: string) => void; + versions: string[]; } & ConnectedProps; const SchemaSection = (props: SchemaSectionProps) => { @@ -37,12 +39,14 @@ const SchemaSection = (props: SchemaSectionProps) => { setIsEditMode, setFirstTabSelected, editorMode, - recalculateContainerHeight, + wrapperSection, checkCompatibilityOfNewSchema, setSchemaSchema, setErrorMessage, setShowErrorPopUp, version, + loadSchemaVersion, + versions, } = props; const [localCode, setLocalCode] = useState(code); @@ -60,6 +64,51 @@ const SchemaSection = (props: SchemaSectionProps) => { setIsEditMode(!isEditMode); }; + const recalculateContainerHeight = (code: string) => { + let basicHeight = 220; + if (wrapperSection && wrapperSection.current) { + wrapperSection.current.style.height = `${calculateHeightOfCodeString(code) + basicHeight}px`; + } else { + wrapperSection.current.style.height = `${basicHeight}px`; + } + if (!isEnrichmentAvailable(code)) { + if ((wrapperSection.current.style.height.replace('px', '') as number) > 600) { + wrapperSection.current.style.height = '600px'; + } + } else { + if ((wrapperSection.current.style.height.replace('px', '') as number) > 700) { + wrapperSection.current.style.height = '700px'; + } + } + }; + + const recalculateCodeHeight = (code: string) => { + const codeHeight = calculateHeightOfCodeString(code); + let height = 478; + if (!isEnrichmentAvailable(code)) { + height = 510; + } + if (codeHeight > height) { + return height; + } + return codeHeight; + }; + + const isEnrichmentAvailable = (code: string): boolean => { + let needsEnrichment = false; + const parsedCode = JSON.parse(code); + (parsedCode.fields || []).map(field => { + if (typeof field.type === 'object' && !Array.isArray(field.type)) { + if (!field.type.doc) { + needsEnrichment = true; + } + } else if (!field.doc) { + needsEnrichment = true; + } + }); + return needsEnrichment; + }; + const checkCompatibility = (_schemaName: string, _code: string, _version: number) => { checkCompatibilityOfNewSchema(_schemaName, _code, _version) .then(() => { @@ -99,14 +148,17 @@ const SchemaSection = (props: SchemaSectionProps) => { > Schema - {/* */} + />
+ {isEnrichmentAvailable(code) && ( + <> +
+
+
+ This schema can be automatically enriched with documentation and saved as a new version. +
+ +
+
Current schema:
+
+ + )} {code && code !== '{}' && ( { diff --git a/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/index.module.scss b/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/index.module.scss index 6edad57e0..27566454c 100644 --- a/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/index.module.scss +++ b/frontend/control-center/src/pages/Schemas/SchemaItem/SchemaDescription/index.module.scss @@ -59,3 +59,70 @@ flex: auto; padding-bottom: 8px; } + +.enrichmentContainer { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 16px; +} + +.enrichmentText { + padding: 8px; +} + +.enrichmentButton { + @include font-s; + border: none; + background-color: var(--color-code-no-edit); + color: var(--color-text-contrast); + font-weight: 600; + cursor: pointer; +} + +.enrichmentSchemaText { + padding: 8px; + @include font-s; + font-size: 12; + font-weight: 800; +} + +.codeContainer { + height: 100%; + width: 100%; + overflow: auto; + padding: 8px; +} + +.version { + display: flex; + p { + @include font-s; + font-size: 13px; + font-weight: 800; + padding: 8px 0; + margin-left: 16px; + color: var(--color-text-contrast); + } + button { + margin: 4px; + height: 24px; + width: 44px; + font-size: 12px; + align-items: center; + justify-content: center; + } + svg { + margin: 0 0 0 6px; + width: 11px; + height: 11px; + } + div { + font-size: 12px; + } +} + +.infoContainer { + display: flex; + background: transparent; +} diff --git a/frontend/control-center/src/pages/Schemas/SchemaItem/index.tsx b/frontend/control-center/src/pages/Schemas/SchemaItem/index.tsx index 071f3fcc8..54367e998 100644 --- a/frontend/control-center/src/pages/Schemas/SchemaItem/index.tsx +++ b/frontend/control-center/src/pages/Schemas/SchemaItem/index.tsx @@ -9,6 +9,7 @@ import SchemaDescription from './SchemaDescription/SchemaDescription'; const mapStateToProps = (state: StateModel) => { return { schemas: state.data.streams.schemas, + schemasVersions: state.data.streams.schemasVersions, }; }; @@ -32,6 +33,7 @@ const SchemaItem = (props: SchemaItemProps) => { addSchemasToSelection, itemSelected, setItemSelected, + schemasVersions, } = props; const [code, setCode] = useState(formatJSON(schemas[schemaName] ? schemas[schemaName].schema : '{}')); @@ -59,6 +61,10 @@ const SchemaItem = (props: SchemaItemProps) => { return 1; }; + const getVersions = (): string[] => { + return schemasVersions[schemaName] || []; + }; + return (
{ setCode={setCode} wrapperSection={wrapperSection} version={getVersion()} + versions={getVersions()} /> )}
diff --git a/frontend/control-center/src/reducers/data/streams/index.ts b/frontend/control-center/src/reducers/data/streams/index.ts index 924a44bb8..417db9ea3 100644 --- a/frontend/control-center/src/reducers/data/streams/index.ts +++ b/frontend/control-center/src/reducers/data/streams/index.ts @@ -10,6 +10,7 @@ const defaultState = { topic_schemas: [], streams: [], schemas: {}, + schemasVersions: {}, streamsInfo: {}, messages: {}, }; @@ -64,6 +65,16 @@ export default function configReducer(state = defaultState, action: Action): Str }, }; } + case getType(actions.setCurrentSchemaVersionsAction): { + const topicName = trimTopicName(action.payload['name']); + return { + ...state, + schemasVersions: { + ...state.schemasVersions, + [topicName]: action.payload.versions, + }, + }; + } case getType(actions.setCurrentStreamInfoAction): { const streamName = action.payload['name']; return { diff --git a/frontend/control-center/src/services/format.ts b/frontend/control-center/src/services/format.ts index 1382230e7..0df49025b 100644 --- a/frontend/control-center/src/services/format.ts +++ b/frontend/control-center/src/services/format.ts @@ -21,7 +21,7 @@ export const isJSON = (string: string): boolean => { export const formatJSON = (jsonString: string): string => { if (jsonString) { - return JSON.stringify(JSON.parse(jsonString), null, 4); + return JSON.stringify(JSON.parse(jsonString), null, 2); } return ''; }; diff --git a/lib/typescript/httpclient/src/client.ts b/lib/typescript/httpclient/src/client.ts index 367a1334d..dc67b2852 100644 --- a/lib/typescript/httpclient/src/client.ts +++ b/lib/typescript/httpclient/src/client.ts @@ -43,6 +43,8 @@ import { LLMConsumersCreateRequestPayload, LLMConsumersCreateResponsePayload, LLMConsumersDeletePayload, + LLMQueryRequestPayload, + LLMQueryResponsePayload, } from './payload'; import { listChannelsDef, @@ -99,6 +101,7 @@ import { llmStatsDef, llmConsumersCreateDef, llmConsumersDeleteDef, + llmQueryDef, } from './endpoints'; import 'isomorphic-fetch'; import FormData from 'form-data'; @@ -342,6 +345,8 @@ export class HttpClient { llmConsumersCreateDef ); + public llmQuery = this.getRequest(llmQueryDef); + private getRequest({endpoint, mapRequest, mapResponse}: EndpointDefinition): ApiRequest { return async (requestPayload: K) => { endpoint = typeof endpoint === 'string' ? endpoint : endpoint(requestPayload); diff --git a/lib/typescript/httpclient/src/endpoints/index.ts b/lib/typescript/httpclient/src/endpoints/index.ts index 4032f0a68..8d847bac6 100644 --- a/lib/typescript/httpclient/src/endpoints/index.ts +++ b/lib/typescript/httpclient/src/endpoints/index.ts @@ -52,3 +52,4 @@ export * from './llmConsumersList'; export * from './llmStats'; export * from './llmInfo'; export * from './llmConsumersDelete'; +export * from './llmQuery'; diff --git a/lib/typescript/httpclient/src/endpoints/llmQuery.ts b/lib/typescript/httpclient/src/endpoints/llmQuery.ts new file mode 100644 index 000000000..37f2bba2d --- /dev/null +++ b/lib/typescript/httpclient/src/endpoints/llmQuery.ts @@ -0,0 +1,6 @@ +import camelcaseKeys from 'camelcase-keys'; + +export const llmQueryDef = { + endpoint: 'llm.query', + mapResponse: response => camelcaseKeys(response), +}; diff --git a/lib/typescript/httpclient/src/payload/LLMQueryRequestPayload.ts b/lib/typescript/httpclient/src/payload/LLMQueryRequestPayload.ts new file mode 100644 index 000000000..cb7b9ec7c --- /dev/null +++ b/lib/typescript/httpclient/src/payload/LLMQueryRequestPayload.ts @@ -0,0 +1,3 @@ +export interface LLMQueryRequestPayload { + query: string; +} diff --git a/lib/typescript/httpclient/src/payload/LLMQueryResponsePayload.ts b/lib/typescript/httpclient/src/payload/LLMQueryResponsePayload.ts new file mode 100644 index 000000000..6780fd96a --- /dev/null +++ b/lib/typescript/httpclient/src/payload/LLMQueryResponsePayload.ts @@ -0,0 +1,6 @@ +export interface LLMQueryResponsePayload { + answer: { + query: string; + result: string; + }; +} diff --git a/lib/typescript/httpclient/src/payload/index.ts b/lib/typescript/httpclient/src/payload/index.ts index 28b11ef5d..94aa9a34a 100644 --- a/lib/typescript/httpclient/src/payload/index.ts +++ b/lib/typescript/httpclient/src/payload/index.ts @@ -43,3 +43,5 @@ export * from './LLMConsumerListPayload'; export * from './LLMInfoPayload'; export * from './LLMStatsPayload'; export * from './LLMConsumersDeletePayload'; +export * from './LLMQueryResponsePayload'; +export * from './LLMQueryRequestPayload'; diff --git a/lib/typescript/model/Streams.ts b/lib/typescript/model/Streams.ts index aee9ed512..749f887f9 100644 --- a/lib/typescript/model/Streams.ts +++ b/lib/typescript/model/Streams.ts @@ -8,6 +8,9 @@ export interface Streams { schemas: { [topicName: string]: Schema; }; + schemasVersions: { + [topicName: string]: string[]; + }; streamsInfo: { [streamName: string]: StreamInfo; };