fix: propagate publish release flag through Go agent endpoints (#17347)

This commit is contained in:
euvre
2026-07-30 19:27:44 +08:00
committed by GitHub
parent d87d9a2881
commit 9519ad7e08
19 changed files with 340 additions and 47 deletions

View File

@@ -122,7 +122,7 @@ docker compose -f docker-compose.yml down
docker compose -f docker-compose.yml up -d
```
## Running with RAGFlow
## Running with RAGFlow
1. Verify that gVisor is properly installed and operational.

View File

@@ -48,7 +48,7 @@ Once your GitHub token is ready, register the external data source within your R
- **Access token:** Paste the PAT generated in section 1.
- **Include Pull Request** Whether to include pull requests in the selected repo.
- **Include Issues** Whether to include issues in the selected repo.
4. Click **Save** to confirm your changes.
4. Click **Save** to confirm your changes.
*RAGFlow validates the connection immediately.*
![GitHub](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/github.jpg)
@@ -66,4 +66,4 @@ Finally, link the connector to a specific knowledge base to begin the RAG proces
3. **Select GitHub source:** Pick the connector you just created.
4. **Trigger initial sync:** - The files from the repo will appear in your file list.
- Select the files and click **Run/parsing**.
- **Parser selection:** For codebases, use the **"Naive"** parser for general text extraction or a specific code-aware template if available in your current version.
- **Parser selection:** For codebases, use the **"Naive"** parser for general text extraction or a specific code-aware template if available in your current version.

View File

@@ -65,7 +65,7 @@ In a knowledge graph, a community is a cluster of entities linked by relationshi
## Quickstart
1. Navigate to the **Configuration** page of your dataset and update:
- Entity types: *Required* - Specifies the entity types in the knowledge graph to generate. You don't have to stick with the default, but you need to customize them for your documents.
- Method: *Optional*
- Entity resolution: *Optional*
@@ -76,12 +76,12 @@ In a knowledge graph, a community is a cluster of entities linked by relationshi
*You can click the pause button in the dropdown to halt the build process when necessary.*
3. Go back to the **Configuration** page:
3. Go back to the **Configuration** page:
*Once a knowledge graph is generated, the **Knowledge graph** field changes from `Not generated` to `Generated at a specific timestamp`. You can delete it by clicking the recycle bin button to the right of the field.*
4. To use the created knowledge graph, do either of the following:
- In the **Chat setting** panel of your chat app, switch on the **Use knowledge graph** toggle.
- If you are using an agent, click the **Retrieval** agent component to specify the dataset(s) and switch on the **Use knowledge graph** toggle.
@@ -101,4 +101,4 @@ All chunks of the created knowledge graph are stored in RAGFlow's document engin
### How to export a created knowledge graph?
Nope. Exporting a created knowledge graph is not supported. If you still consider this feature essential, please [raise an issue](https://github.com/infiniflow/ragflow/issues) explaining your use case and its importance.
Nope. Exporting a created knowledge graph is not supported. If you still consider this feature essential, please [raise an issue](https://github.com/infiniflow/ragflow/issues) explaining your use case and its importance.

View File

@@ -78,7 +78,7 @@ A random seed. Click **+** to change the seed value.
## Quickstart
1. Navigate to the **Configuration** page of your dataset and update:
- Prompt: *Optional* - We recommend that you keep it as-is until you understand the mechanism behind.
- Max token: *Optional*
- Threshold: *Optional*
@@ -88,8 +88,8 @@ A random seed. Click **+** to change the seed value.
*You can click the pause button in the dropdown to halt the build process when necessary.*
3. Go back to the **Configuration** page:
3. Go back to the **Configuration** page:
*The **RAPTOR** field changes from `Not generated` to `Generated at a specific timestamp` when a RAPTOR hierarchical tree structure is generated. You can delete it by clicking the recycle bin button to the right of the field.*
4. Once a RAPTOR hierarchical tree structure is generated, your chat assistant and **Retrieval** agent component will use it for retrieval as a default.

View File

@@ -30,7 +30,7 @@ The system's default chat model is used to summarize clustered content. Before p
2. Enable **PageIndex**.
3. To use this technique during retrieval, do either of the following:
- In the **Chat setting** panel of your chat app, switch on the **PageIndex** toggle.
- If you are using an Agent, click the **Retrieval** agent component to specify the dataset(s) and switch on the **PageIndex** toggle.
@@ -38,4 +38,4 @@ The system's default chat model is used to summarize clustered content. Before p
### Will previously parsed files be searched using the directory enhancement feature once I enable `PageIndex`?
No. Only files parsed after you enable **PageIndex** will be searched using the directory enhancement feature. To apply this feature to files parsed before enabling **PageIndex**, you must reparse them.
No. Only files parsed after you enable **PageIndex** will be searched using the directory enhancement feature. To apply this feature to files parsed before enabling **PageIndex**, you must reparse them.

View File

@@ -33,4 +33,4 @@ Through this dual-layer structure of "precise localization + contextual suppleme
3. This configuration applies to the **Chunker** component when it comes to ingestion pipeline settings:
![](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/child_chunking_chunker.png)
![](https://raw.githubusercontent.com/infiniflow/ragflow-docs/main/images/child_chunking_chunker.png)

View File

@@ -31,4 +31,4 @@ Ensure that your metadata is in JSON format; otherwise, your updates will not be
### Can I set metadata for multiple documents at once?
From v0.23.0 onwards, you can set metadata for each document individually or have the LLM auto-generate metadata for multiple files. See [Extract metadata](./advanced/auto_metadata.md) for details.
From v0.23.0 onwards, you can set metadata for each document individually or have the LLM auto-generate metadata for multiple files. See [Extract metadata](./advanced/auto_metadata.md) for details.

View File

@@ -11,7 +11,7 @@ RAGFlow model provider management allows you to connect online models, local mod
## Get model API key
RAGFlow supports most mainstream LLMs. Please refer to [Supported Models](../../guides/models/supported_models.mdx) for a complete list of supported models. You will need to apply for your model API key online.
RAGFlow supports most mainstream LLMs. Please refer to [Supported Models](../../guides/models/supported_models.mdx) for a complete list of supported models. You will need to apply for your model API key online.
:::note
If you find your online LLM is not on the list, don't feel disheartened. The list is expanding, and you can [file a feature request](https://github.com/infiniflow/ragflow/issues/new?assignees=&labels=feature+request&projects=&template=feature_request.yml&title=%5BFeature+Request%5D%3A+) with us! Alternatively, if you have customized or locally-deployed models, you can [bind them to RAGFlow using Ollama, Xinference, or LocalAI](./deploy_local_llm.mdx).
@@ -119,4 +119,4 @@ The following model types usually work together for retrieval and generation:
## Supported model list
See [Supported Models](./supported_models.mdx).
See [Supported Models](./supported_models.mdx).

View File

@@ -490,6 +490,14 @@ func (dao *UserCanvasDAO) UpdateFields(ctx context.Context, db *gorm.DB, canvasI
return result.RowsAffected, result.Error
}
// UpdateFieldsTx is the transactional variant of UpdateFields. Used by
// service.AgentService.UpdateAgent so the canvas row update and the
// version-row save commit atomically in one transaction.
func (dao *UserCanvasDAO) UpdateFieldsTx(tx *gorm.DB, canvasID string, fields map[string]interface{}) (int64, error) {
result := tx.Model(&entity.UserCanvas{}).Where("id = ?", canvasID).Updates(fields)
return result.RowsAffected, result.Error
}
// UpdateTags updates a canvas's comma-separated tags by canvas ID.
func (dao *UserCanvasDAO) UpdateTags(ctx context.Context, db *gorm.DB, canvasID, tags string) (int64, error) {
result := db.WithContext(ctx).Model(&entity.UserCanvas{}).Where("id = ?", canvasID).Update("tags", tags)

View File

@@ -100,6 +100,50 @@ func (dao *UserCanvasVersionDAO) GetLatest(ctx context.Context, db *gorm.DB, can
return &v, nil
}
// GetLatestReleased returns the most recently updated released version of
// canvasID, or ErrUserCanvasVersionNotFound when nothing was ever published.
// Mirrors the released-version lookup in Python's get_agent handler.
func (dao *UserCanvasVersionDAO) GetLatestReleased(canvasID string) (*entity.UserCanvasVersion, error) {
var v entity.UserCanvasVersion
err := DB.Where("user_canvas_id = ? AND `release` = ?", canvasID, true).
Order("update_time DESC").
First(&v).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUserCanvasVersionNotFound
}
return nil, err
}
return &v, nil
}
// GetLatestReleaseTimes returns MAX(create_time) of released versions per
// canvas for the given canvas IDs. Mirrors the release_time aggregation in
// Python's UserCanvasService.get_list.
func (dao *UserCanvasVersionDAO) GetLatestReleaseTimes(canvasIDs []string) (map[string]int64, error) {
result := make(map[string]int64, len(canvasIDs))
if len(canvasIDs) == 0 {
return result, nil
}
type row struct {
UserCanvasID string
ReleaseTime int64
}
var rows []row
err := DB.Model(&entity.UserCanvasVersion{}).
Select("user_canvas_id, MAX(create_time) AS release_time").
Where("user_canvas_id IN ? AND `release` = ?", canvasIDs, true).
Group("user_canvas_id").
Scan(&rows).Error
if err != nil {
return nil, err
}
for _, r := range rows {
result[r.UserCanvasID] = r.ReleaseTime
}
return result, nil
}
// Delete removes a single version by id. No-op when the row is absent.
func (dao *UserCanvasVersionDAO) Delete(ctx context.Context, db *gorm.DB, id string) error {
return db.WithContext(ctx).Where("id = ?", id).Delete(&entity.UserCanvasVersion{}).Error

View File

@@ -291,7 +291,22 @@ func (h *AgentHandler) GetAgent(c *gin.Context) {
if row != nil {
row.DSL = dslpkg.NormalizeForCanvas(row.DSL)
}
common.SuccessWithData(c, row, "success")
// Attach last_publish_time so the front-end can render when the agent
// was last published (Python get_agent parity).
lastPublishTime, err := h.agentService.GetLastPublishTime(c.Request.Context(), canvasID)
if err != nil {
ec, em := mapAgentError(err)
common.ResponseWithCodeData(c, ec, nil, em)
return
}
common.SuccessWithData(c, &agentDetailResponse{UserCanvas: row, LastPublishTime: lastPublishTime}, "success")
}
// agentDetailResponse wraps the canvas row with derived fields that Python's
// get_agent handler adds to the response.
type agentDetailResponse struct {
*entity.UserCanvas
LastPublishTime *int64 `json:"last_publish_time,omitempty"`
}
// updateAgentRequest is the wire shape for PUT /api/v1/agents/:canvas_id.

View File

@@ -412,6 +412,7 @@ type AgentItem struct {
CanvasCategory string `json:"canvas_category"`
CreateTime *int64 `json:"create_time,omitempty"`
UpdateTime *int64 `json:"update_time,omitempty"`
ReleaseTime *int64 `json:"release_time,omitempty"`
}
// ListAgentsResponse is the response body for GET /api/v1/agents.
@@ -503,6 +504,25 @@ func (s *AgentService) ListAgents(ctx context.Context, userID string, keywords s
for i, c := range canvases {
items[i] = toAgentItem(c)
}
// Attach the latest release time per canvas so agent cards can render
// the "published at" line (Python UserCanvasService.get_list parity).
if len(items) > 0 {
canvasIDs := make([]string, 0, len(items))
for _, item := range items {
canvasIDs = append(canvasIDs, item.ID)
}
releaseTimes, err := s.versionDAO.GetLatestReleaseTimes(canvasIDs)
if err != nil {
return nil, common.CodeServerError, fmt.Errorf("failed to get release times: %w", err)
}
for _, item := range items {
if t, ok := releaseTimes[item.ID]; ok {
item.ReleaseTime = &t
}
}
}
return &ListAgentsResponse{Canvas: items, Total: total}, common.CodeSuccess, nil
}
@@ -651,6 +671,20 @@ func (s *AgentService) GetAgent(ctx context.Context, userID, canvasID string) (*
return s.loadCanvasForUser(ctx, userID, canvasID)
}
// GetLastPublishTime returns the update_time of the most recently updated
// released version, or nil when the canvas has never been published.
// Mirrors the last_publish_time computation in Python's get_agent handler.
func (s *AgentService) GetLastPublishTime(ctx context.Context, canvasID string) (*int64, error) {
version, err := s.versionDAO.GetLatestReleased(canvasID)
if err != nil {
if errors.Is(err, dao.ErrUserCanvasVersionNotFound) {
return nil, nil
}
return nil, err
}
return version.UpdateTime, nil
}
// UpdateAgent applies a draft patch to user_canvas. Settings updates may omit
// dsl; in that case the existing draft DSL must be preserved.
//
@@ -681,6 +715,23 @@ func (s *AgentService) UpdateAgent(ctx context.Context, userID, canvasID string,
updates[key] = value
}
}
// Publish flow: the front-end sends release ("true"/true) together with
// dsl via PUT, and Python's update_agent coerces it with
// `bool(req.get("release", ""))` — any non-empty string is truthy.
// Mirror that here so the canvas row and the new version row both carry
// the release flag; an absent release keeps parity with Python, which
// always writes the coerced value (False when missing).
release := false
if value, ok := patch["release"]; ok && value != nil {
switch v := value.(type) {
case bool:
release = v
case string:
release = v != ""
}
}
updates["release"] = release
if title, ok := updatedAgentTitle(canvasInstance, updates); ok {
canvasCategory := updatedAgentCanvasCategory(canvasInstance, updates)
if existing, err := s.canvasDAO.GetByUserAndTitle(ctx, dao.DB, userID, title, canvasCategory); err != nil {
@@ -701,16 +752,10 @@ func (s *AgentService) UpdateAgent(ctx context.Context, userID, canvasID string,
updates["dsl"] = entity.JSONMap(dslpkg.NormalizeForCanvas(dslMap))
}
_, err = s.canvasDAO.UpdateFields(ctx, dao.DB, canvasID, updates)
if err != nil {
if dao.IsDuplicateKeyErr(err) {
if title, ok := updatedAgentTitle(canvasInstance, updates); ok {
return agentTitleAlreadyExistsError(title)
}
return errors.New("agent title already exists")
}
return fmt.Errorf("update agent %s: %w", canvasID, err)
}
// Build the version options up front (it only reads the user nickname)
// so the canvas row update and the version save can share one
// transaction below.
var versionOpts *dao.SaveOrReplaceLatestVersionOptions
if dslValue, ok := updates["dsl"]; ok {
dsl, ok := dslValue.(entity.JSONMap)
if !ok {
@@ -722,9 +767,33 @@ func (s *AgentService) UpdateAgent(ctx context.Context, userID, canvasID string,
} else if canvasInstance.Title != nil {
title = *canvasInstance.Title
}
if _, err := s.saveOrReplaceVersion(ctx, userID, canvasID, dsl, title, nil, false); err != nil {
return fmt.Errorf("update agent %s: save version: %w", canvasID, err)
opts := s.saveOrReplaceVersionOptions(ctx, userID, canvasID, dsl, title, nil, release)
versionOpts = &opts
}
// The canvas release/DSL update and the version save must commit
// atomically: if the version write fails after the canvas row is
// committed, the canvas is left published (or unpublished) without a
// matching version state.
err = dao.DB.Transaction(func(tx *gorm.DB) error {
if _, err := s.canvasDAO.UpdateFieldsTx(tx, canvasID, updates); err != nil {
return err
}
if versionOpts != nil {
if _, err := s.versionDAO.SaveOrReplaceLatestTx(ctx, tx, *versionOpts); err != nil {
return fmt.Errorf("save version: %w", err)
}
}
return nil
})
if err != nil {
if dao.IsDuplicateKeyErr(err) {
if title, ok := updatedAgentTitle(canvasInstance, updates); ok {
return agentTitleAlreadyExistsError(title)
}
return errors.New("agent title already exists")
}
return fmt.Errorf("update agent %s: %w", canvasID, err)
}
return nil
}
@@ -851,10 +920,6 @@ func (s *AgentService) PublishAgent(ctx context.Context, userID, canvasID string
return row, nil
}
func (s *AgentService) saveOrReplaceVersion(ctx context.Context, userID, canvasID string, dsl entity.JSONMap, title string, description *string, release bool) (*entity.UserCanvasVersion, error) {
return s.versionDAO.SaveOrReplaceLatest(ctx, dao.DB, s.saveOrReplaceVersionOptions(ctx, userID, canvasID, dsl, title, description, release))
}
func (s *AgentService) saveOrReplaceVersionOptions(ctx context.Context, userID, canvasID string, dsl entity.JSONMap, title string, description *string, release bool) dao.SaveOrReplaceLatestVersionOptions {
nickname, err := s.userDAO.GetNicknameByID(ctx, dao.DB, userID)
if err != nil || strings.TrimSpace(nickname) == "" {

View File

@@ -1682,6 +1682,166 @@ func TestUpdateAgentDSLCreatesAndReplacesDraftVersion(t *testing.T) {
}
}
func TestUpdateAgentReleaseTrueMarksCanvasAndVersionReleased(t *testing.T) {
setupAgentSessionServiceTest(t)
if err := dao.DB.Create(&entity.User{ID: "user-1", Nickname: "owner", Email: "owner@test.com"}).Error; err != nil {
t.Fatalf("failed to seed user: %v", err)
}
if err := dao.DB.Create(&entity.UserCanvas{
ID: "canvas-update-release",
UserID: "user-1",
Title: sptr("Release Agent"),
CanvasCategory: "agent_canvas",
DSL: entity.JSONMap{},
}).Error; err != nil {
t.Fatalf("failed to seed canvas: %v", err)
}
ctx := t.Context()
// The front-end publish flow sends release as the string "true".
if err := NewAgentService().UpdateAgent(ctx, "user-1", "canvas-update-release", map[string]interface{}{
"title": "Release Agent",
"release": "true",
"dsl": map[string]interface{}{
"graph": map[string]interface{}{
"nodes": []interface{}{map[string]interface{}{"id": "begin"}},
"edges": []interface{}{},
},
"components": map[string]interface{}{
"begin": map[string]interface{}{
"obj": map[string]interface{}{"component_name": "Begin"},
},
},
},
}); err != nil {
t.Fatalf("UpdateAgent failed: %v", err)
}
persisted, err := dao.NewUserCanvasDAO().GetByID(ctx, dao.DB, "canvas-update-release")
if err != nil {
t.Fatalf("failed to reload canvas: %v", err)
}
if !persisted.Release {
t.Fatal("canvas release flag was not set after publish update")
}
versions, err := dao.NewUserCanvasVersionDAO().ListByCanvasID(ctx, dao.DB, "canvas-update-release")
if err != nil {
t.Fatalf("failed to list versions: %v", err)
}
if len(versions) != 1 {
t.Fatalf("expected 1 version, got %d", len(versions))
}
if !versions[0].Release {
t.Fatal("version created by publish update is not marked released")
}
lastPublishTime, err := NewAgentService().GetLastPublishTime(ctx, "canvas-update-release")
if err != nil {
t.Fatalf("GetLastPublishTime failed: %v", err)
}
if lastPublishTime == nil {
t.Fatal("GetLastPublishTime returned nil after publish")
}
if versions[0].UpdateTime == nil || *lastPublishTime != *versions[0].UpdateTime {
t.Fatalf("last publish time = %v, want version update_time %v", lastPublishTime, versions[0].UpdateTime)
}
}
func TestUpdateAgentWithoutReleaseKeepsCanvasUnreleased(t *testing.T) {
setupAgentSessionServiceTest(t)
if err := dao.DB.Create(&entity.User{ID: "user-1", Nickname: "owner", Email: "owner@test.com"}).Error; err != nil {
t.Fatalf("failed to seed user: %v", err)
}
if err := dao.DB.Create(&entity.UserCanvas{
ID: "canvas-update-draft",
UserID: "user-1",
Title: sptr("Draft Agent"),
CanvasCategory: "agent_canvas",
DSL: entity.JSONMap{},
Release: true,
}).Error; err != nil {
t.Fatalf("failed to seed canvas: %v", err)
}
// Python parity: update_agent coerces a missing release to False and
// always writes it back to the canvas row.
ctx := t.Context()
if err := NewAgentService().UpdateAgent(ctx, "user-1", "canvas-update-draft", map[string]interface{}{
"title": "Draft Agent",
"dsl": map[string]interface{}{
"graph": map[string]interface{}{
"nodes": []interface{}{map[string]interface{}{"id": "begin"}},
"edges": []interface{}{},
},
"components": map[string]interface{}{
"begin": map[string]interface{}{
"obj": map[string]interface{}{"component_name": "Begin"},
},
},
},
}); err != nil {
t.Fatalf("UpdateAgent failed: %v", err)
}
persisted, err := dao.NewUserCanvasDAO().GetByID(ctx, dao.DB, "canvas-update-draft")
if err != nil {
t.Fatalf("failed to reload canvas: %v", err)
}
if persisted.Release {
t.Fatal("canvas release flag should be reset to false when release is absent")
}
if lastPublishTime, err := NewAgentService().GetLastPublishTime(ctx, "canvas-update-draft"); err != nil {
t.Fatalf("GetLastPublishTime failed: %v", err)
} else if lastPublishTime != nil {
t.Fatalf("GetLastPublishTime = %v, want nil without released version", lastPublishTime)
}
}
func TestListAgentsIncludesReleaseTime(t *testing.T) {
setupAgentSessionServiceTest(t)
if err := dao.DB.Create(&entity.User{ID: "user-1", Nickname: "owner", Email: "owner@test.com"}).Error; err != nil {
t.Fatalf("failed to seed user: %v", err)
}
createAgentSessionTestCanvas(t, "canvas-listed-released", "user-1")
createAgentSessionTestCanvas(t, "canvas-listed-draft", "user-1")
releaseTime := int64(1753335149000)
if err := dao.DB.Create(&entity.UserCanvasVersion{
ID: "version-listed-released",
UserCanvasID: "canvas-listed-released",
Title: sptr("owner_Test Agent_2026-07-24 00:00:00"),
Release: true,
DSL: entity.JSONMap{},
BaseModel: entity.BaseModel{CreateTime: &releaseTime},
}).Error; err != nil {
t.Fatalf("failed to seed released version: %v", err)
}
ctx := t.Context()
resp, code, err := NewAgentService().ListAgents(ctx, "user-1", "", 1, 30, "create_time", true, nil, "", "", nil)
if err != nil || code != common.CodeSuccess {
t.Fatalf("ListAgents failed: code=%v err=%v", code, err)
}
var released, draft *AgentItem
for _, item := range resp.Canvas {
switch item.ID {
case "canvas-listed-released":
released = item
case "canvas-listed-draft":
draft = item
}
}
if released == nil || draft == nil {
t.Fatalf("expected both canvases in list, got %#v", resp.Canvas)
}
if released.ReleaseTime == nil || *released.ReleaseTime != releaseTime {
t.Fatalf("ReleaseTime = %v, want %d", released.ReleaseTime, releaseTime)
}
if draft.ReleaseTime != nil {
t.Fatalf("draft canvas ReleaseTime = %v, want nil", draft.ReleaseTime)
}
}
func TestPublishAgentUpdatesCanvasAndReleasedVersion(t *testing.T) {
setupAgentSessionServiceTest(t)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -75,8 +75,8 @@ export function NextMessageInput({
null,
);
const [enableThinking, setEnableThinking] = useState(
() => storage.getThinkingLevel(),
const [enableThinking, setEnableThinking] = useState(() =>
storage.getThinkingLevel(),
);
const [enableInternet, setEnableInternet] = useState(false);

View File

@@ -1039,7 +1039,10 @@ export const useFetchKnowledgeList = (
};
},
getNextPageParam: (lastPage, allPages) => {
const loaded = allPages.reduce((total, page) => total + page.items.length, 0);
const loaded = allPages.reduce(
(total, page) => total + page.items.length,
0,
);
return loaded < lastPage.total ? allPages.length + 1 : undefined;
},
});

View File

@@ -24,9 +24,10 @@ export const useShowLog = (documents: IDocumentInfo[]) => {
const liveDoc = liveDocs?.[0];
const logInfo = useMemo(() => {
const source = liveDoc ?? documents.find(
(item: IDocumentInfo) => item.id === record?.id,
) ?? record;
const source =
liveDoc ??
documents.find((item: IDocumentInfo) => item.id === record?.id) ??
record;
let log: ILogInfo = {
taskId: source?.id,
fileName: source?.name || '-',
@@ -41,9 +42,7 @@ export const useShowLog = (documents: IDocumentInfo[]) => {
fileSize: formatBytes(source.size || 0),
processBeginAt: formatDate(source.process_begin_at),
chunkNumber: source.chunk_count,
duration: formatSecondsToHumanReadable(
source.process_duration || 0,
),
duration: formatSecondsToHumanReadable(source.process_duration || 0),
status: source.run as RunningStatus,
details: source.progress_msg,
};

View File

@@ -169,9 +169,7 @@ export function ModelsSection(props: ModelsSectionProps) {
});
// 6a. Model selection for batch verify.
const [selectedModels, setSelectedModels] = useState<Set<string>>(
new Set(),
);
const [selectedModels, setSelectedModels] = useState<Set<string>>(new Set());
const toggleModel = useCallback((name: string) => {
setSelectedModels((prev) => {

View File

@@ -125,7 +125,8 @@ request.interceptors.response.use(
}
}
const skipErrorNotification = (response.config as any)?.skipGlobalErrorNotification;
const skipErrorNotification = (response.config as any)
?.skipGlobalErrorNotification;
if (data?.code === 100 && !skipErrorNotification) {
message.error(data?.message);
} else if (data?.code === 401) {