[ACS-8664] generic question redirection to hx insight (#10174)

* ACS-8664 Loading HX insight url

* ACS-8664 Added documentation for loading config of Knowledge Retrieval

* ACS-8664 Unit tests

* ACS-8664 Fixed unit tests

* ACS-8664 Fixed unit tests after rebase

* ACS-8664 Addressed comment
This commit is contained in:
AleksanderSklorz
2024-09-09 11:55:01 +02:00
committed by Aleksander Sklorz
parent 13fc92e62c
commit 0a717d612f
10 changed files with 184 additions and 72 deletions

View File

@@ -24,6 +24,9 @@ Manages search AI in Content Services.
Get an answer to specific question. Get an answer to specific question.
- _questionId:_ `string` - The ID of the question to get an answer for. - _questionId:_ `string` - The ID of the question to get an answer for.
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`AiAnswerEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#aianswerentry)`>` - AiAnswerEntry object containing the answer. - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`AiAnswerEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#aianswerentry)`>` - AiAnswerEntry object containing the answer.
- **getConfig**(): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`KnowledgeRetrievalConfigEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#knowledgeretrievalconfigentry)`>`<br/>
Get the knowledge retrieval configuration.
- **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`KnowledgeRetrievalConfigEntry`](../../../lib/js-api/src/api/content-rest-api/docs/SearchAiApi.md#knowledgeretrievalconfigentry)`>` - KnowledgeRetrievalConfigEntry object containing the configuration.
- **checkSearchAvailability**(selectedNodesState: `SelectionState`, maxSelectedNodes: `number`): `string`<br/> - **checkSearchAvailability**(selectedNodesState: `SelectionState`, maxSelectedNodes: `number`): `string`<br/>
Check if using of search is possible (if all conditions are met). Check if using of search is possible (if all conditions are met).
- _selectedNodesState:_ `SelectionState` - information about selected nodes. - _selectedNodesState:_ `SelectionState` - information about selected nodes.

View File

@@ -16,7 +16,7 @@
*/ */
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { AiAnswerEntry, Node, QuestionModel, QuestionRequest } from '@alfresco/js-api'; import { AiAnswerEntry, KnowledgeRetrievalConfigEntry, Node, QuestionModel, QuestionRequest } from '@alfresco/js-api';
import { ContentTestingModule } from '../../testing/content.testing.module'; import { ContentTestingModule } from '../../testing/content.testing.module';
import { SearchAiService } from './search-ai.service'; import { SearchAiService } from './search-ai.service';
import { SearchAiInputState } from '../models/search-ai-input-state'; import { SearchAiInputState } from '../models/search-ai-input-state';
@@ -79,6 +79,23 @@ describe('SearchAiService', () => {
}); });
}); });
describe('getConfig', () => {
it('should load knowledge retrieval configuration', (done) => {
const config: KnowledgeRetrievalConfigEntry = {
entry: {
knowledgeRetrievalUrl: 'https://some-url'
}
};
spyOn(service.searchAiApi, 'getConfig').and.returnValue(Promise.resolve(config));
service.getConfig().subscribe((configResponse) => {
expect(configResponse).toBe(config);
expect(service.searchAiApi.getConfig).toHaveBeenCalled();
done();
});
});
});
describe('updateSearchAiInputState', () => { describe('updateSearchAiInputState', () => {
it('should trigger toggleSearchAiInput$', () => { it('should trigger toggleSearchAiInput$', () => {
const state: SearchAiInputState = { const state: SearchAiInputState = {

View File

@@ -16,7 +16,7 @@
*/ */
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AiAnswerEntry, QuestionModel, QuestionRequest, SearchAiApi } from '@alfresco/js-api'; import { AiAnswerEntry, KnowledgeRetrievalConfigEntry, QuestionModel, QuestionRequest, SearchAiApi } from '@alfresco/js-api';
import { AlfrescoApiService } from '@alfresco/adf-core'; import { AlfrescoApiService } from '@alfresco/adf-core';
import { BehaviorSubject, from, Observable } from 'rxjs'; import { BehaviorSubject, from, Observable } from 'rxjs';
import { SelectionState } from '@alfresco/adf-extensions'; import { SelectionState } from '@alfresco/adf-extensions';
@@ -70,6 +70,15 @@ export class SearchAiService {
return from(this.searchAiApi.getAnswer(questionId)); return from(this.searchAiApi.getAnswer(questionId));
} }
/**
* Get the knowledge retrieval configuration.
*
* @returns KnowledgeRetrievalConfigEntry object containing the configuration.
*/
getConfig(): Observable<KnowledgeRetrievalConfigEntry> {
return from(this.searchAiApi.getConfig());
}
/** /**
* Check if using of search is possible (if all conditions are met). * Check if using of search is possible (if all conditions are met).
* *

View File

@@ -15,10 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import { QuestionModel } from '../model/questionModel'; import { QuestionModel, QuestionRequest, AiAnswerEntry, KnowledgeRetrievalConfigEntry } from '../model';
import { BaseApi } from '../../hxi-connector-api/api/base.api'; import { BaseApi } from '../../hxi-connector-api/api/base.api';
import { QuestionRequest } from '../model/questionRequest';
import { AiAnswerEntry } from '../model';
/** /**
* Search AI API. * Search AI API.
@@ -52,4 +50,15 @@ export class SearchAiApi extends BaseApi {
path: `questions/${questionId}/answers/-default-` path: `questions/${questionId}/answers/-default-`
}); });
} }
/**
* Get the knowledge retrieval configuration.
*
* @returns KnowledgeRetrievalConfigEntry object containing the configuration.
*/
getConfig(): Promise<KnowledgeRetrievalConfigEntry> {
return this.get({
path: '/config/-default-'
});
}
} }

View File

@@ -1,9 +1,10 @@
# SearchAiApi # SearchAiApi
| Method | HTTP request | Description | | Method | HTTP request | Description |
|-------------------------|----------------------------|-------------------------------------| |-------------------------|----------------------------|--------------------------------------------|
| [ask](#ask) | **GET** /questions | Ask a question to the AI. | | [ask](#ask) | **GET** /questions | Ask a question to the AI. |
| [getAnswer](#getAnswer) | **GET** /answers/-default- | Get an answer to specific question. | | [getAnswer](#getAnswer) | **GET** /answers/-default- | Get an answer to specific question. |
| [getConfig](#getConfig) | **GET** /config/-default- | Get the knowledge retrieval configuration. |
## ask ## ask
@@ -97,6 +98,33 @@ searchAiApi.getAnswer('some question id').then((answer) => {
**Return type**: [AiAnswerEntry](#AiAnswerEntry) **Return type**: [AiAnswerEntry](#AiAnswerEntry)
## getConfig
Get the knowledge retrieval configuration. For example:
```json
{
"entry": {
"knowledgeRetrievalUrl": "https://some-url"
}
}
```
**Example**
```javascript
import { AlfrescoApi, AgentsApi } from '@alfresco/js-api';
const alfrescoApi = new AlfrescoApi(/*..*/);
const searchAiApi = new SearchAiApi(alfrescoApi);
searchAiApi.getConfig().then((answer) => {
console.log('API called successfully. Returned data: ', answer.entry.knowledgeRetrievalUrl);
});
```
**Return type**: [KnowledgeRetrievalConfigEntry](#KnowledgeRetrievalConfigEntry)
# Models # Models
## AiAnswerEntry ## AiAnswerEntry
@@ -153,3 +181,19 @@ searchAiApi.getAnswer('some question id').then((answer) => {
| **question** | string | | **question** | string |
| **nodeIds** | string[] | | **nodeIds** | string[] |
| **agentId** | string | | **agentId** | string |
## KnowledgeRetrievalConfigEntry
**Properties**
| Name | Type |
|-------|-------------------------------------------------------|
| entry | [KnowledgeRetrievalConfig](#KnowledgeRetrievalConfig) |
## KnowledgeRetrievalConfig
**Properties**
| Name | Type |
|-----------------------|--------|
| knowledgeRetrievalUrl | string |

View File

@@ -99,6 +99,8 @@ export * from './groupMemberPagingList';
export * from './groupMembershipBodyCreate'; export * from './groupMembershipBodyCreate';
export * from './groupPaging'; export * from './groupPaging';
export * from './groupPagingList'; export * from './groupPagingList';
export * from './knowledgeRetrievalConfig';
export * from './knowledgeRetrievalConfigEntry';
export * from './modelError'; export * from './modelError';
export * from './networkQuota'; export * from './networkQuota';
export * from './node'; export * from './node';

View File

@@ -0,0 +1,20 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface KnowledgeRetrievalConfig {
knowledgeRetrievalUrl: string;
}

View File

@@ -0,0 +1,22 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { KnowledgeRetrievalConfig } from './knowledgeRetrievalConfig';
export interface KnowledgeRetrievalConfigEntry {
entry: KnowledgeRetrievalConfig;
}

View File

@@ -44,27 +44,17 @@ describe('SearchAiApi', () => {
{ {
question: 'some question 1', question: 'some question 1',
nodeIds: ['some node id 1'], nodeIds: ['some node id 1'],
agentId: 'some id 1' agentId: 'id1'
},
{
question: 'some question 2',
nodeIds: ['some node id 2', 'some node id 3'],
agentId: 'some id 2'
} }
]) ])
.then((questions) => { .then((questions) => {
assert.deepStrictEqual(questions, [ assert.deepStrictEqual(questions, {
{ questionId: 'some id 1',
questionId: 'some id 1', question: 'some question 1',
question: 'some question 1', restrictionQuery: {
restrictionQuery: 'some node id 1' nodesIds: ['some node id 1']
},
{
questionId: 'some id 2',
question: 'some question 2',
restrictionQuery: 'some node id 2,some node id 3'
} }
]); });
done(); done();
}); });
}); });
@@ -91,4 +81,19 @@ describe('SearchAiApi', () => {
}); });
}); });
}); });
describe('getConfig', () => {
it('should load knowledge retrieval configuration', (done) => {
searchAiMock.mockGetConfig200Response();
searchAiApi.getConfig().then((config) => {
assert.deepStrictEqual(config, {
entry: {
knowledgeRetrievalUrl: 'https://some-url'
}
});
done();
});
});
});
}); });

View File

@@ -21,68 +21,49 @@ import nock from 'nock';
export class SearchAiMock extends BaseMock { export class SearchAiMock extends BaseMock {
mockGetAsk200Response(): void { mockGetAsk200Response(): void {
nock(this.host, { encodedQueryParams: true }) nock(this.host, { encodedQueryParams: true })
.get('/alfresco/api/-default-/private/hxi/versions/1/questions', [ .post('/alfresco/api/-default-/private/hxi/versions/1/agents/id1/questions', [
{ {
question: 'some question 1', question: 'some question 1',
restrictionQuery: 'some node id 1' restrictionQuery: {
}, nodesIds: ['some node id 1']
{ }
question: 'some question 2',
restrictionQuery: 'some node id 2,some node id 3'
} }
]) ])
.reply(200, [ .reply(200, {
{ entry: {
question: 'some question 1', question: 'some question 1',
questionId: 'some id 1', questionId: 'some id 1',
restrictionQuery: 'some node id 1' restrictionQuery: {
}, nodesIds: ['some node id 1']
{ }
question: 'some question 2',
questionId: 'some id 2',
restrictionQuery: 'some node id 2,some node id 3'
} }
]); });
} }
mockGetAnswer200Response(): void { mockGetAnswer200Response(): void {
nock(this.host, { encodedQueryParams: true }) nock(this.host, { encodedQueryParams: true })
.get('/alfresco/api/-default-/private/hxi/versions/1/answers/-default-?questionId=id1') .get('/alfresco/api/-default-/private/hxi/versions/1/questions/id1/answers/-default-')
.reply(200, { .reply(200, {
list: { entry: {
pagination: { answer: 'Some answer 1',
count: 2, questionId: 'some id 1',
hasMoreItems: false, references: [
skipCount: 0,
maxItems: 100
},
entries: [
{ {
entry: { referenceId: 'some reference id 1',
answer: 'Some answer 1', referenceText: 'some reference text 1'
questionId: 'some id 1',
references: [
{
referenceId: 'some reference id 1',
referenceText: 'some reference text 1'
}
]
}
},
{
entry: {
answer: 'Some answer 2',
questionId: 'some id 2',
references: [
{
referenceId: 'some reference id 2',
referenceText: 'some reference text 2'
}
]
}
} }
] ]
} }
}); });
} }
mockGetConfig200Response(): void {
nock(this.host, { encodedQueryParams: true })
.get('/alfresco/api/-default-/private/hxi/versions/1/config/-default-')
.reply(200, {
entry: {
knowledgeRetrievalUrl: 'https://some-url'
}
});
}
} }