Skip to content

Commit

Permalink
Merge pull request #42 from meta-d/develop
Browse files Browse the repository at this point in the history
Version 2.5.1
  • Loading branch information
meta-d authored Aug 7, 2024
2 parents e415624 + 626926f commit 42d4ff6
Show file tree
Hide file tree
Showing 51 changed files with 407 additions and 329 deletions.
2 changes: 1 addition & 1 deletion .deploy/api/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ocap-server",
"author": "Metad",
"version": "2.5.0",
"version": "2.5.1",
"scripts": {
"start": "nx serve",
"build": "nx build",
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,23 @@ English | [中文](./README_zh.md)
# Metad Analytics Platform

## 💡 What's New
### ChatBI: Natural Language-Driven Business Intelligence Analysis

[ChatBI](https://mtda.cloud/en/docs/chatbi) is an innovative feature we are introducing, combining chat functionality with business intelligence (BI) analysis capabilities. It offers users a more intuitive and convenient data analysis experience through natural language interaction. The main features of ChatBI include:

1. **Natural Language Querying**: Users can ask questions in natural language and get data analysis results directly without needing to understand complex query languages. This feature allows even non-technical personnel to easily obtain data insights.

2. **Multi-Turn Conversations**: Supports multi-turn conversations, allowing for continuous, context-aware interactions. The system remembers previous conversation content, enabling deeper and more precise data analysis.

3. **Support for Various Large Language Models**: ChatBI integrates multiple mainstream large language models, such as ChatGPT and Llama, enhancing the accuracy of natural language understanding and generation to meet different business needs and language support.
4. **Security and Access Control**: Provides strict data security and access control to ensure the protection of sensitive data.
5. **Integration with Multiple Data Sources**: Supports connection and integration with data from various sources, such as databases, cloud services, and SAP ERP systems, providing users with rich data sources.

[More details](https://mtda.cloud/en/blog/releases-2-5-chatbi/)

[ChatBI_Demo.mp4](https://github.com/user-attachments/assets/5f7c84be-2307-43cf-8342-bce39524e37d)

### Copilot Command Agents
🎉🎉 New version brings several exciting new features, particularly a comprehensive upgrade to [Copilot Command](https://www.mtda.cloud/en/docs/server/copilot/#commands) and [Business Roles](https://www.mtda.cloud/en/docs/server/copilot/#business-roles).

- [New Version 2.4 - Copilot Multi-Agent Command](https://www.mtda.cloud/en/blog/releases-2-4-copilot-multi-agent).
Expand Down
15 changes: 15 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@

## 💡 新功能

### ChatBI:自然语言驱动的商业智能分析

[ChatBI](https://mtda.cloud/docs/chatbi) 是我们新推出的一个创新功能,它将聊天功能与商业智能(BI)分析能力相结合,通过自然语言交互的方式,为用户提供更加直观和便捷的数据分析体验。以下是 ChatBI 的主要特点:
1. **自然语言查询 (Natural Language Querying)**:用户可以使用自然语言提问,直接获取数据分析结果,无需掌握复杂的查询语言。这一功能让即便是非技术人员,也能轻松获取数据洞察。
2. **多轮对话 (Multi-Turn Conversations)**:支持多轮对话功能,允许用户进行连续的、上下文相关的交互。系统能够记住前面的对话内容,使数据分析更加深入和精准。
3. **支持各种大语言模型 (Various Large Language Models)**:ChatBI 集成了多种主流大语言模型,如 ChatGPT 和 Llama,提升了自然语言理解和生成的准确性,满足不同业务需求和语言支持。
4. **安全与权限管理 (Security and Access Control)**:提供严格的数据安全和权限管理,确保敏感数据的保护。
5. **集成多数据源 (Integration with Multiple Data Sources)**:支持连接和集成来自不同来源的数据,如数据库、云端服务、SAP ERP 系统等,为用户提供丰富的数据来源。

[更多详情](https://mtda.cloud/blog/releases-2-5-chatbi)

[ChatBI_Demo.mp4](https://github.com/user-attachments/assets/5f7c84be-2307-43cf-8342-bce39524e37d)

### 副驾命令智能体

🎉🎉 新版本带来了一些令人兴奋的新功能,特别是对 [Copilot Command](https://www.mtda.cloud/docs/server/copilot/#命令)[Business Roles](https://www.mtda.cloud/docs/server/copilot/#业务角色) 的全面升级。

- [新版本 2.4 - 副驾多智能体命令](https://www.mtda.cloud/blog/releases-2-4-copilot-multi-agent/)
Expand Down
2 changes: 1 addition & 1 deletion apps/cloud/src/app/features/chatbi/chatbi.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export class ChatbiService {
}

updateQuestionAnswer(key: string, answer: QuestionAnswer) {
this._updateConversation(this.conversationKey(), (state) => {
this.updateConversation((state) => {
const index = state.messages.findIndex((message) => message.id === key)
if (index > -1) {
state.messages[index] = {
Expand Down
8 changes: 5 additions & 3 deletions apps/cloud/src/app/features/chatbi/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { nonBlank, provideOcapCore } from '@metad/ocap-angular/core'
import { DisplayBehaviour } from '@metad/ocap-core'
import { StoryExplorerModule } from '@metad/story'
import { TranslateModule } from '@ngx-translate/core'
import { NGXLogger } from 'ngx-logger'
import { injectQueryParams } from 'ngxtension/inject-query-params'
import { filter, map, switchMap } from 'rxjs'
import { ChatBIConversationService, routeAnimations } from '../../@core'
Expand Down Expand Up @@ -49,6 +50,7 @@ export class ChatbiHomeComponent {
readonly conversationService = inject(ChatBIConversationService)
readonly router = inject(Router)
readonly route = inject(ActivatedRoute)
readonly logger = inject(NGXLogger)
readonly conversationId = injectQueryParams('id')

readonly modelId = model<string>(null)
Expand Down Expand Up @@ -127,13 +129,13 @@ export class ChatbiHomeComponent {
}

async openExplore(message: CopilotChatMessage, answer: QuestionAnswer) {
console.log(answer)
this.logger.debug('openExplorer', answer)
this.showExplorer.set(true)
this.explore.set({ ...answer, key: message.id })
this.explore.set({ ...structuredClone(answer), key: message.id })
}

closeExplorer(event?: QuestionAnswer) {
console.log(event)
this.logger.debug('closeExplorer', event)
this.showExplorer.set(false)
if (event) {
this.chatbiService.updateQuestionAnswer(this.explore().key, event)
Expand Down
11 changes: 6 additions & 5 deletions apps/cloud/src/app/features/chatbi/input/input.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,20 +85,21 @@
</div>

<ng-template #menu>
<div class="example-menu" cdkMenu>
<div class="ngm-chatbi__conversations-menu" cdkMenu>
@for (item of conversations(); track item.key) {
<button
cdkMenuItemCheckbox
class="example-menu-item w-full"
class="ngm-chatbi__conversations-menu-item w-full"
[cdkMenuItemChecked]="item.key === conversation()?.key"
(click)="setConversation(item.key)"
>
<div class="flex justify-start items-center text-left min-w-[100px]">
<div class="flex justify-start items-center text-left min-w-[100px] max-w-[600px] overflow-hidden text-ellipsis">
@if (conversations().length > 1) {
<mat-icon class="cursor-pointer rounded-full hover:bg-neutral-300" ngmAppearance="danger" displayDensity="compact"
<mat-icon class="cursor-pointer mr-2 rounded-full hover:bg-neutral-300 dark:hover:bg-neutral-600" ngmAppearance="danger" displayDensity="cosy"
(click)="deleteConversation(item.key)">close</mat-icon>
}
<span class="flex-1 text-ellipsis overflow-hidden">{{item.name || ('PAC.ChatBI.Empty' | translate: {Default: 'Empty'}) }}</span>
<span class="flex-1 text-ellipsis overflow-hidden"
[title]="item.name">{{item.name || ('PAC.ChatBI.Empty' | translate: {Default: 'Empty'}) }}</span>
</div>
</button>
}
Expand Down
14 changes: 14 additions & 0 deletions apps/cloud/src/app/features/chatbi/input/input.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,17 @@
.ngm-colpilot__input.disabled {
@apply opacity-50;
}

.ngm-chatbi__conversations-menu {
.ngm-chatbi__conversations-menu-item {
&[role='menuitemradio'][aria-checked='true'] {
@apply bg-zinc-200 dark:bg-zinc-600;
}
&[role='menuitemcheckbox'][aria-checked='true'] {
@apply bg-zinc-200 dark:bg-zinc-600;
}
&:active {
@apply bg-zinc-400 dark:bg-zinc-500;
}
}
}
5 changes: 3 additions & 2 deletions apps/cloud/src/app/features/project/project.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable, computed, effect, inject, signal } from '@angular/core'
import { Injectable, computed, inject, signal } from '@angular/core'
import { toObservable, toSignal } from '@angular/core/rxjs-interop'
import {
BusinessAreasService,
Expand All @@ -11,7 +11,7 @@ import {
import { dirtyCheckWith, nonBlank } from '@metad/core'
import { NgmDSCoreService } from '@metad/ocap-angular/core'
import { WasmAgentService } from '@metad/ocap-angular/wasm-agent'
import { MDCube, isEntitySet, isEqual, negate } from '@metad/ocap-core'
import { MDCube, isEntitySet, isEqual, negate, nonNullable } from '@metad/ocap-core'
import { Store, createStore, withProps } from '@ngneat/elf'
import { stateHistory } from '@ngneat/elf-state-history'
import { cloneDeep } from 'lodash-es'
Expand Down Expand Up @@ -69,6 +69,7 @@ export class ProjectService {
readonly modelDetails = signal<Record<string, ISemanticModel>>({})

readonly modelCubes$ = this.models$.pipe(
filter(nonNullable),
tap(async (models) => {
for await (const model of models) {
await this.registerModel(model.key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export class ProjectComponent extends TranslationBaseComponent {

readonly projectService = inject(ProjectService)
readonly collectionService = inject(CollectionService)
// readonly copilotContext = inject(NgmCopilotContextToken)
readonly dsCoreService = inject(NgmDSCoreService)
readonly wasmAgent = inject(WasmAgentService)
readonly appService = inject(AppService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ export class ModelMembersCubeComponent {

async refresh() {
const cube = this.cube().name
// const dimensions = this.dimensions()

this.loading.set(true)
if (this.entity()?.id) {
Expand All @@ -117,27 +116,25 @@ export class ModelMembersCubeComponent {

if (this.selectedDims()) {
for (const name of this.selectedDims()) {
// storeMembers[name] = []

let storeMembers = []
const hierarchy = getEntityHierarchy(this.cube().entityType, name)
// const dimension = dimensions.find((dim) => dim.name === name)
// for (const hierarchy of dimension.hierarchies) {
const members = await tryHttp(
this.modelService.selectHierarchyMembers(cube, { dimension: hierarchy.dimension, hierarchy: hierarchy.name }),
this.toastrService
)
if (members) {
storeMembers = storeMembers.concat(members)
}
// }
if (!hierarchy) {
this.toastrService.error('PAC.MODEL.CanntFoundHierarchy', null, {Default: `Can't found hierarchy '${name}'`, value: name})
} else {
const members = await tryHttp(
this.modelService.selectHierarchyMembers(cube, { dimension: hierarchy.dimension, hierarchy: hierarchy.name }),
this.toastrService
)
if (members) {
storeMembers = storeMembers.concat(members)
}

this.members.update((members) => ({
...members,
[name]: storeMembers
}))
this.members.update((members) => ({
...members,
[name]: storeMembers
}))
}
}

this.loaded.set(true)
}

Expand Down
14 changes: 10 additions & 4 deletions apps/cloud/src/app/features/story/copilot/calculation/command.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Signal, inject } from '@angular/core'
import { CopilotAgentType } from '@metad/copilot'
import { CopilotAgentType, referencesCommandName } from '@metad/copilot'
import { injectCopilotCommand } from '@metad/copilot-angular'
import { DataSettings } from '@metad/ocap-core'
import { TranslateService } from '@ngx-translate/core'
import { injectExampleRetriever } from 'apps/cloud/src/app/@core/copilot'
import { NGXLogger } from 'ngx-logger'
import { injectCreateCalculationGraph } from './graph'
import {
CALCULATION_COMMAND_NAME,
CONDITIONAL_AGGREGATION_AGENT_NAME,
FORMULA_AGENT_NAME,
MEASURE_CONTROL_AGENT_NAME,
Expand All @@ -22,8 +24,11 @@ export function injectCalculationGraphCommand(

const createGraph = injectCreateCalculationGraph(defaultDataSettings, callback)

const commandName = 'calculation'
return injectCopilotCommand(commandName, {
const referencesRetriever = injectExampleRetriever(referencesCommandName(CALCULATION_COMMAND_NAME), {
k: 3,
vectorStore: null
})
return injectCopilotCommand(CALCULATION_COMMAND_NAME, {
alias: 'cc',
description: translate.instant('PAC.Story.CommandCalculationDesc', {
Default: 'Describe logic of the calculation you want'
Expand All @@ -37,7 +42,8 @@ export function injectCalculationGraphCommand(
CONDITIONAL_AGGREGATION_AGENT_NAME,
VARIANCE_AGENT_NAME,
MEASURE_CONTROL_AGENT_NAME
]
],
referencesRetriever
},
createGraph
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,10 @@ When finished, respond FINISH. Choose strategically to minimize the number of st
{role}
{context}`

)
The dataset context:
{context}
Reference Documentations:
{references}`)

const createFormulaAgent = await createFormulaWorker({
llm,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Team } from "@metad/copilot"
import { MEMBER_RETRIEVER_TOOL_NAME } from "@metad/core"

export const CALCULATION_COMMAND_NAME = 'calculation'
export const SUPERVISOR_NAME = 'Supervisor'

export const FORMULA_AGENT_NAME = 'FormulaMeasureAgent'
Expand Down
3 changes: 0 additions & 3 deletions apps/cloud/src/app/features/story/copilot/page/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ export function injectCreatePageGraph() {
Reference Documentations:
{{references}}
Step 1. Create a new page in story dashboard.
Step 2. 根据提供的 Cube context 和分析主题逐个向 dashboard 中添加 widgets.
Widget 类型分为 Text, FilterBar, InputControl, Table, Chart, and KPI。
- 页面 layout 布局默认是 40 * 40.
Expand Down
8 changes: 4 additions & 4 deletions apps/cloud/src/app/features/story/copilot/page/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ export function injectCreatePageTools() {
description: 'Create a title widget in story dashboard page.',
schema: z.object({
widget: createWidgetSchema({}),
styling: WidgetStyleSchema,
options: z.object({
text: z.string().describe('The text content of widget')
}).describe('The options of text widget')
text: z.string().describe('The text content of this widget')
}).describe('Options of this text widget'),
styling: WidgetStyleSchema.optional().describe('Css styles of this widget'),
}),
func: async ({ widget, styling, options }) => {
logger.debug(`Execute copilot action 'createTitle':`, `widget:`, widget)
logger.debug(`Execute copilot action 'createTitle':`, `widget:`, widget, options, styling)
storyService.createStoryWidget({
...widget,
component: WidgetComponentType.Text,
Expand Down
40 changes: 15 additions & 25 deletions apps/cloud/src/app/features/story/copilot/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,18 @@ export const MeasureControlSchema = z.object({
displayBehaviour: z.enum([DisplayBehaviour.descriptionAndId, DisplayBehaviour.descriptionOnly, DisplayBehaviour.idOnly]).optional().describe(`The display behaviour of measure options`),
})

export const RestrictedMeasureBikes = {
measure: 'Sales',
dimensions: [
{
dimension: '[Product]',
hierarchy: '[Product.Category]',
members: [
{
key: '[Bikes]'
}
]
}
],
enableConstantSelection: true
// slicers: [
// {
// dimension: { dimension: '[Product]' },
// members: [
// {
// key: '[Product].[Bikes]'
// }
// ]
// }
// ]
}
export const AnalyticalGridOptionsSchema = z.object({
showToolbar: z.boolean().optional().describe('Show toolbar of table grid'),
strip: z.boolean().optional().describe('Strip rows of table grid'),
grid: z.boolean().optional().describe('Table is grid style'),
paging: z.boolean().optional().describe('Table is paging'),
pageSize: z.number().optional().describe('Page size'),
sticky: z.boolean().optional().describe('Sticky header of table'),
initialRowLevel: z.number().optional().describe('Inital row level number'),
initialColumnLevel: z.number().optional().describe('Inital column level number'),
sortable: z.boolean().optional().describe('Columns of table is sortable'),
selectable: z.boolean().optional().describe('Columns of table is selectable'),
digitsInfo: z.string().default('0.1-1').optional().describe('Digits Info of measure value'),
unit: z.string().optional().describe('Unit of measure value'),
currencyCode: z.string().optional().describe('Currency Code of measure value'),
})
Loading

0 comments on commit 42d4ff6

Please sign in to comment.