mirror of
https://github.com/langgenius/dify.git
synced 2026-11-10 13:26:58 +08:00
feat(agent): add opt-in show thinking toggle for Agent v2 chat (#45427)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
67 files changed
+407
-27
No files matched your search
@@ -475,6 +475,7 @@ class Parameters(BaseModel):
|
||||
speech_to_text: FeatureToggleObject
|
||||
text_to_speech: TextToSpeechObject
|
||||
retriever_resource: FeatureToggleObject
|
||||
show_thoughts: FeatureToggleObject = Field(default_factory=lambda: {"enabled": False})
|
||||
annotation_reply: FeatureToggleObject
|
||||
more_like_this: FeatureToggleObject
|
||||
user_input_form: UserInputFormList
|
||||
|
||||
@@ -24,6 +24,7 @@ class AppParametersDict(TypedDict):
|
||||
speech_to_text: FeatureToggleDict
|
||||
text_to_speech: FeatureToggleDict
|
||||
retriever_resource: FeatureToggleDict
|
||||
show_thoughts: FeatureToggleDict
|
||||
annotation_reply: FeatureToggleDict
|
||||
more_like_this: FeatureToggleDict
|
||||
user_input_form: list[dict[str, Any]]
|
||||
@@ -36,7 +37,9 @@ def get_parameters_from_feature_dict(
|
||||
*, features_dict: Mapping[str, Any], user_input_form: list[dict[str, Any]]
|
||||
) -> AppParametersDict:
|
||||
"""
|
||||
Mapping from feature dict to webapp parameters
|
||||
Map a feature mapping and input form to webapp parameters without mutating either.
|
||||
|
||||
Missing or null show_thoughts is exposed as disabled for legacy configurations.
|
||||
"""
|
||||
return {
|
||||
"opening_statement": features_dict.get("opening_statement"),
|
||||
@@ -45,6 +48,7 @@ def get_parameters_from_feature_dict(
|
||||
"speech_to_text": features_dict.get("speech_to_text", {"enabled": False}),
|
||||
"text_to_speech": features_dict.get("text_to_speech", {"enabled": False}),
|
||||
"retriever_resource": features_dict.get("retriever_resource", {"enabled": False}),
|
||||
"show_thoughts": features_dict.get("show_thoughts") or {"enabled": False},
|
||||
"annotation_reply": features_dict.get("annotation_reply", {"enabled": False}),
|
||||
"more_like_this": features_dict.get("more_like_this", {"enabled": False}),
|
||||
"user_input_form": user_input_form,
|
||||
|
||||
@@ -606,6 +606,8 @@ class AgentSoulAppFeaturesConfig(AgentFlexibleConfig):
|
||||
speech_to_text: AgentFeatureToggleConfig | None = None
|
||||
text_to_speech: AgentTextToSpeechFeatureConfig | None = None
|
||||
retriever_resource: AgentFeatureToggleConfig | None = None
|
||||
# Missing or null preserves the default of hiding model thoughts in chat.
|
||||
show_thoughts: AgentFeatureToggleConfig | None = None
|
||||
sensitive_word_avoidance: AgentSensitiveWordAvoidanceFeatureConfig | None = None
|
||||
file_upload: AgentFileUploadFeatureConfig = Field(default_factory=AgentFileUploadFeatureConfig)
|
||||
|
||||
|
||||
Generated
+2
@@ -20753,6 +20753,7 @@ Visibility and lifecycle scope of an Agent record.
|
||||
| opening_statement | string | | No |
|
||||
| retriever_resource | [AgentFeatureToggleConfig](#agentfeaturetoggleconfig) | | No |
|
||||
| sensitive_word_avoidance | [AgentSensitiveWordAvoidanceFeatureConfig](#agentsensitivewordavoidancefeatureconfig) | | No |
|
||||
| show_thoughts | [AgentFeatureToggleConfig](#agentfeaturetoggleconfig) | | No |
|
||||
| speech_to_text | [AgentFeatureToggleConfig](#agentfeaturetoggleconfig) | | No |
|
||||
| suggested_questions | [ string ] | | No |
|
||||
| suggested_questions_after_answer | [AgentSuggestedQuestionsAfterAnswerFeatureConfig](#agentsuggestedquestionsafteranswerfeatureconfig) | | No |
|
||||
@@ -32215,6 +32216,7 @@ Enum class for parameter type.
|
||||
| opening_statement | | | No |
|
||||
| retriever_resource | { **"enabled"**: boolean } | | Yes |
|
||||
| sensitive_word_avoidance | { **"enabled"**: boolean } | | Yes |
|
||||
| show_thoughts | { **"enabled"**: boolean } | | No |
|
||||
| speech_to_text | { **"enabled"**: boolean } | | Yes |
|
||||
| suggested_questions | [ string ] | | Yes |
|
||||
| suggested_questions_after_answer | { **"enabled"**: boolean } | | Yes |
|
||||
|
||||
Generated
+1
@@ -7380,6 +7380,7 @@ Form input definition.
|
||||
| opening_statement | | | No |
|
||||
| retriever_resource | { **"enabled"**: boolean } | | Yes |
|
||||
| sensitive_word_avoidance | { **"enabled"**: boolean } | | Yes |
|
||||
| show_thoughts | { **"enabled"**: boolean } | | No |
|
||||
| speech_to_text | { **"enabled"**: boolean } | | Yes |
|
||||
| suggested_questions | [ string ] | | Yes |
|
||||
| suggested_questions_after_answer | { **"enabled"**: boolean } | | Yes |
|
||||
|
||||
Generated
+1
@@ -2071,6 +2071,7 @@ Form input definition.
|
||||
| opening_statement | | | No |
|
||||
| retriever_resource | { **"enabled"**: boolean } | | Yes |
|
||||
| sensitive_word_avoidance | { **"enabled"**: boolean } | | Yes |
|
||||
| show_thoughts | { **"enabled"**: boolean } | | No |
|
||||
| speech_to_text | { **"enabled"**: boolean } | | Yes |
|
||||
| suggested_questions | [ string ] | | Yes |
|
||||
| suggested_questions_after_answer | { **"enabled"**: boolean } | | Yes |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from controllers.common.fields import Parameters, Site
|
||||
from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict
|
||||
from models.model import IconType
|
||||
@@ -13,6 +15,22 @@ def test_parameters_model_round_trip() -> None:
|
||||
assert model.model_dump(mode="json") == parameters
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enabled", [False, True])
|
||||
def test_parameters_serializes_show_thoughts(enabled: bool) -> None:
|
||||
parameters = get_parameters_from_feature_dict(
|
||||
features_dict={"show_thoughts": {"enabled": enabled}}, user_input_form=[]
|
||||
)
|
||||
|
||||
assert Parameters.model_validate(parameters).model_dump(mode="json")["show_thoughts"] == {"enabled": enabled}
|
||||
|
||||
|
||||
def test_parameters_defaults_show_thoughts_for_legacy_payload() -> None:
|
||||
parameters = dict(get_parameters_from_feature_dict(features_dict={}, user_input_form=[]))
|
||||
parameters.pop("show_thoughts")
|
||||
|
||||
assert Parameters.model_validate(parameters).model_dump(mode="json")["show_thoughts"] == {"enabled": False}
|
||||
|
||||
|
||||
def test_site_icon_url_uses_signed_url_for_image_icon() -> None:
|
||||
site = Site(
|
||||
title="Example",
|
||||
|
||||
@@ -490,6 +490,7 @@ def test_migrated_saved_message_and_parameter_handlers_dispatch_through_full_adm
|
||||
"retriever_resource",
|
||||
"annotation_reply",
|
||||
"more_like_this",
|
||||
"show_thoughts",
|
||||
"sensitive_word_avoidance",
|
||||
"file_upload",
|
||||
)
|
||||
|
||||
@@ -2490,6 +2490,7 @@ class TestTrialPreviewCases:
|
||||
"retriever_resource": {"enabled": False},
|
||||
"annotation_reply": {"enabled": False},
|
||||
"more_like_this": {"enabled": False},
|
||||
"show_thoughts": {"enabled": False},
|
||||
"user_input_form": _INPUT_FORM,
|
||||
"sensitive_word_avoidance": {"enabled": False, "type": "", "configs": []},
|
||||
"file_upload": {"enabled": False, "number_limits": 0},
|
||||
|
||||
@@ -21,6 +21,15 @@ type _DefaultedFeatureKey = Literal[
|
||||
class TestGetParametersFromFeatureDict:
|
||||
"""Test suite for get_parameters_from_feature_dict"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("features", "enabled"),
|
||||
[({}, False), ({"show_thoughts": None}, False), ({"show_thoughts": {"enabled": True}}, True)],
|
||||
)
|
||||
def test_show_thoughts_parameters(self, features: dict[str, object], enabled: bool) -> None:
|
||||
result = parameters_mapping.get_parameters_from_feature_dict(features_dict=features, user_input_form=[])
|
||||
|
||||
assert result["show_thoughts"] == {"enabled": enabled}
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Override file limits on the shared typed config."""
|
||||
|
||||
@@ -4,6 +4,7 @@ from pydantic import ValidationError
|
||||
from core.workflow.file_reference import build_file_reference
|
||||
from models.agent_config_entities import (
|
||||
AgentKnowledgeSpaceConfig,
|
||||
AgentSoulAppFeaturesConfig,
|
||||
AgentSoulConfig,
|
||||
AgentSoulKnowledgeConfig,
|
||||
AgentSoulModelSettings,
|
||||
@@ -16,6 +17,23 @@ from models.agent_config_entities import (
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("payload", [{}, {"show_thoughts": None}, {"show_thoughts": {}}])
|
||||
def test_show_thoughts_defaults_to_disabled(payload: dict[str, object]) -> None:
|
||||
features = AgentSoulAppFeaturesConfig.model_validate(payload)
|
||||
|
||||
assert features.show_thoughts is None or features.show_thoughts.enabled is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enabled", [False, True])
|
||||
def test_show_thoughts_round_trips_in_agent_soul(enabled: bool) -> None:
|
||||
soul = AgentSoulConfig.model_validate({"app_features": {"show_thoughts": {"enabled": enabled}}})
|
||||
|
||||
restored = AgentSoulConfig.model_validate_json(soul.model_dump_json())
|
||||
|
||||
assert restored.app_features.show_thoughts is not None
|
||||
assert restored.app_features.show_thoughts.enabled is enabled
|
||||
|
||||
|
||||
def test_agent_soul_model_settings_preserves_plugin_declared_parameters() -> None:
|
||||
settings = AgentSoulModelSettings.model_validate(
|
||||
{
|
||||
|
||||
Generated
-10
@@ -1032,16 +1032,6 @@
|
||||
"count": 91
|
||||
}
|
||||
},
|
||||
"web/app/components/base/chat/chat/answer/agent-roster-response-content.tsx": {
|
||||
"better-tailwindcss/enforce-logical-properties": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"web/app/components/base/chat/chat/answer/index.tsx": {
|
||||
"better-tailwindcss/enforce-logical-properties": {
|
||||
"count": 7
|
||||
}
|
||||
},
|
||||
"web/app/components/base/chat/chat/answer/more.tsx": {
|
||||
"better-tailwindcss/enforce-logical-properties": {
|
||||
"count": 3
|
||||
|
||||
@@ -1581,6 +1581,7 @@ export type AgentSoulAppFeaturesConfig = {
|
||||
opening_statement?: string | null
|
||||
retriever_resource?: AgentFeatureToggleConfig | null
|
||||
sensitive_word_avoidance?: AgentSensitiveWordAvoidanceFeatureConfig | null
|
||||
show_thoughts?: AgentFeatureToggleConfig | null
|
||||
speech_to_text?: AgentFeatureToggleConfig | null
|
||||
suggested_questions?: Array<string> | null
|
||||
suggested_questions_after_answer?: AgentSuggestedQuestionsAfterAnswerFeatureConfig | null
|
||||
|
||||
@@ -3133,6 +3133,7 @@ export const zAgentSoulAppFeaturesConfig = z.object({
|
||||
opening_statement: z.string().nullish(),
|
||||
retriever_resource: zAgentFeatureToggleConfig.nullish(),
|
||||
sensitive_word_avoidance: zAgentSensitiveWordAvoidanceFeatureConfig.nullish(),
|
||||
show_thoughts: zAgentFeatureToggleConfig.nullish(),
|
||||
speech_to_text: zAgentFeatureToggleConfig.nullish(),
|
||||
suggested_questions: z.array(z.string()).nullish(),
|
||||
suggested_questions_after_answer: zAgentSuggestedQuestionsAfterAnswerFeatureConfig.nullish(),
|
||||
|
||||
@@ -3525,6 +3525,7 @@ export type AgentSoulAppFeaturesConfig = {
|
||||
opening_statement?: string | null
|
||||
retriever_resource?: AgentFeatureToggleConfig | null
|
||||
sensitive_word_avoidance?: AgentSensitiveWordAvoidanceFeatureConfig | null
|
||||
show_thoughts?: AgentFeatureToggleConfig | null
|
||||
speech_to_text?: AgentFeatureToggleConfig | null
|
||||
suggested_questions?: Array<string> | null
|
||||
suggested_questions_after_answer?: AgentSuggestedQuestionsAfterAnswerFeatureConfig | null
|
||||
|
||||
@@ -6090,6 +6090,7 @@ export const zAgentSoulAppFeaturesConfig = z.object({
|
||||
opening_statement: z.string().nullish(),
|
||||
retriever_resource: zAgentFeatureToggleConfig.nullish(),
|
||||
sensitive_word_avoidance: zAgentSensitiveWordAvoidanceFeatureConfig.nullish(),
|
||||
show_thoughts: zAgentFeatureToggleConfig.nullish(),
|
||||
speech_to_text: zAgentFeatureToggleConfig.nullish(),
|
||||
suggested_questions: z.array(z.string()).nullish(),
|
||||
suggested_questions_after_answer: zAgentSuggestedQuestionsAfterAnswerFeatureConfig.nullish(),
|
||||
|
||||
@@ -299,6 +299,9 @@ export type Parameters = {
|
||||
sensitive_word_avoidance: {
|
||||
enabled?: boolean
|
||||
}
|
||||
show_thoughts?: {
|
||||
enabled?: boolean
|
||||
}
|
||||
speech_to_text: {
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
@@ -609,6 +609,11 @@ export const zParameters = z.object({
|
||||
sensitive_word_avoidance: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
show_thoughts: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
speech_to_text: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
|
||||
@@ -628,6 +628,7 @@ export type AgentSoulAppFeaturesConfig = {
|
||||
opening_statement?: string | null
|
||||
retriever_resource?: AgentFeatureToggleConfig | null
|
||||
sensitive_word_avoidance?: AgentSensitiveWordAvoidanceFeatureConfig | null
|
||||
show_thoughts?: AgentFeatureToggleConfig | null
|
||||
speech_to_text?: AgentFeatureToggleConfig | null
|
||||
suggested_questions?: Array<string> | null
|
||||
suggested_questions_after_answer?: AgentSuggestedQuestionsAfterAnswerFeatureConfig | null
|
||||
|
||||
@@ -1546,6 +1546,7 @@ export const zAgentSoulAppFeaturesConfig = z.object({
|
||||
opening_statement: z.string().nullish(),
|
||||
retriever_resource: zAgentFeatureToggleConfig.nullish(),
|
||||
sensitive_word_avoidance: zAgentSensitiveWordAvoidanceFeatureConfig.nullish(),
|
||||
show_thoughts: zAgentFeatureToggleConfig.nullish(),
|
||||
speech_to_text: zAgentFeatureToggleConfig.nullish(),
|
||||
suggested_questions: z.array(z.string()).nullish(),
|
||||
suggested_questions_after_answer: zAgentSuggestedQuestionsAfterAnswerFeatureConfig.nullish(),
|
||||
|
||||
@@ -339,6 +339,9 @@ export type Parameters = {
|
||||
sensitive_word_avoidance: {
|
||||
enabled?: boolean
|
||||
}
|
||||
show_thoughts?: {
|
||||
enabled?: boolean
|
||||
}
|
||||
speech_to_text: {
|
||||
enabled?: boolean
|
||||
}
|
||||
@@ -1007,6 +1010,7 @@ export type AgentSoulAppFeaturesConfig = {
|
||||
opening_statement?: string | null
|
||||
retriever_resource?: AgentFeatureToggleConfig | null
|
||||
sensitive_word_avoidance?: AgentSensitiveWordAvoidanceFeatureConfig | null
|
||||
show_thoughts?: AgentFeatureToggleConfig | null
|
||||
speech_to_text?: AgentFeatureToggleConfig | null
|
||||
suggested_questions?: Array<string> | null
|
||||
suggested_questions_after_answer?: AgentSuggestedQuestionsAfterAnswerFeatureConfig | null
|
||||
|
||||
@@ -896,6 +896,11 @@ export const zParameters = z.object({
|
||||
sensitive_word_avoidance: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
show_thoughts: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
speech_to_text: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
@@ -1936,6 +1941,7 @@ export const zAgentSoulAppFeaturesConfig = z.object({
|
||||
opening_statement: z.string().nullish(),
|
||||
retriever_resource: zAgentFeatureToggleConfig.nullish(),
|
||||
sensitive_word_avoidance: zAgentSensitiveWordAvoidanceFeatureConfig.nullish(),
|
||||
show_thoughts: zAgentFeatureToggleConfig.nullish(),
|
||||
speech_to_text: zAgentFeatureToggleConfig.nullish(),
|
||||
suggested_questions: z.array(z.string()).nullish(),
|
||||
suggested_questions_after_answer: zAgentSuggestedQuestionsAfterAnswerFeatureConfig.nullish(),
|
||||
|
||||
@@ -3484,6 +3484,9 @@ export type Parameters = {
|
||||
sensitive_word_avoidance: {
|
||||
enabled?: boolean
|
||||
}
|
||||
show_thoughts?: {
|
||||
enabled?: boolean
|
||||
}
|
||||
speech_to_text: {
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
@@ -4966,6 +4966,11 @@ export const zParameters = z.object({
|
||||
sensitive_word_avoidance: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
show_thoughts: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
speech_to_text: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
|
||||
@@ -705,6 +705,9 @@ export type Parameters = {
|
||||
sensitive_word_avoidance: {
|
||||
enabled?: boolean
|
||||
}
|
||||
show_thoughts?: {
|
||||
enabled?: boolean
|
||||
}
|
||||
speech_to_text: {
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
+5
@@ -1458,6 +1458,11 @@ export const zParameters = z.object({
|
||||
sensitive_word_avoidance: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
show_thoughts: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
speech_to_text: z.object({
|
||||
enabled: z.boolean().optional(),
|
||||
}),
|
||||
|
||||
+47
@@ -42,6 +42,53 @@ const thinkingOnlyItem = {
|
||||
} satisfies ChatItem
|
||||
|
||||
describe('AgentRosterResponseContent', () => {
|
||||
it('shows trimmed plain-text thoughts in collapsed history activity only when enabled', async () => {
|
||||
const user = userEvent.setup()
|
||||
const thought = {
|
||||
...thinkingOnlyItem.agent_response_parts[0]!.thought,
|
||||
thought: ' \n<think>First</think>\n**Second** &\n ',
|
||||
}
|
||||
const item: ChatItem = {
|
||||
...thinkingOnlyItem,
|
||||
agent_response_parts: undefined,
|
||||
agent_thoughts: [thought],
|
||||
}
|
||||
const { rerender } = render(<AgentRosterResponseContent item={item} />)
|
||||
expect(screen.queryByRole('button', { name: 'Thinking' })).not.toBeInTheDocument()
|
||||
|
||||
rerender(<AgentRosterResponseContent item={item} showThoughts />)
|
||||
const toggle = screen.getByRole('button', { name: 'Thinking' })
|
||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
||||
await user.click(toggle)
|
||||
const text = screen.getByText('<think>First</think> **Second** &')
|
||||
expect(text.textContent).toBe('<think>First</think>\n**Second** &')
|
||||
expect(text.children).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('collapses enabled live thoughts once they become history', async () => {
|
||||
const user = userEvent.setup()
|
||||
const { rerender } = render(
|
||||
<AgentRosterResponseContent item={thinkingOnlyItem} responding showThoughts />,
|
||||
)
|
||||
expect(screen.getByText('internal thought should not render')).toBeVisible()
|
||||
rerender(
|
||||
<AgentRosterResponseContent
|
||||
item={{
|
||||
...thinkingOnlyItem,
|
||||
agent_response_parts: undefined,
|
||||
agent_thoughts: [thinkingOnlyItem.agent_response_parts[0]!.thought],
|
||||
}}
|
||||
showThoughts
|
||||
/>,
|
||||
)
|
||||
expect(screen.getByRole('button', { name: 'Thinking' })).toHaveAttribute(
|
||||
'aria-expanded',
|
||||
'false',
|
||||
)
|
||||
await user.click(screen.getByRole('button', { name: 'Thinking' }))
|
||||
expect(await screen.findByText('internal thought should not render')).toBeVisible()
|
||||
})
|
||||
|
||||
it('renders a verified receipt as a working in-answer link, but not a forged receipt', async () => {
|
||||
const user = userEvent.setup()
|
||||
const receiptId = `kfs_${'a'.repeat(32)}`
|
||||
|
||||
@@ -80,6 +80,27 @@ describe('Answer Component', () => {
|
||||
expect(screen.getByText('streamed answer')).toHaveTextContent('streamed answer')
|
||||
})
|
||||
|
||||
it.each([true, false])(
|
||||
'should pass the configured show thoughts setting (%s) to custom agent content',
|
||||
(enabled) => {
|
||||
render(
|
||||
<Answer
|
||||
{...defaultProps}
|
||||
item={{
|
||||
id: 'msg-with-parts',
|
||||
content: '',
|
||||
isAnswer: true,
|
||||
agent_response_parts: [{ type: 'message', content: 'streamed answer' }],
|
||||
}}
|
||||
config={{ show_thoughts: { enabled } } as ComponentProps<typeof Answer>['config']}
|
||||
renderAgentContent={({ showThoughts }) => <p>{`thoughts ${showThoughts}`}</p>}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText(`thoughts ${enabled}`)).toBeInTheDocument()
|
||||
},
|
||||
)
|
||||
|
||||
it('should render annotation edit title', async () => {
|
||||
render(
|
||||
<Answer
|
||||
|
||||
@@ -14,6 +14,7 @@ type AgentRosterResponseContentProps = {
|
||||
item: ChatItem
|
||||
responding?: boolean
|
||||
content?: string
|
||||
showThoughts?: boolean
|
||||
}
|
||||
|
||||
const SHELL_TOOL_NAMES = new Set(['shell_run', 'shell_wait', 'shell_input', 'shell_interrupt'])
|
||||
@@ -108,11 +109,13 @@ function hashString(value: string) {
|
||||
return (hash >>> 0).toString(36)
|
||||
}
|
||||
|
||||
function hasVisibleActivity(thought: ThoughtItem) {
|
||||
return !!thought.tool || !!thought.message_files?.length
|
||||
function hasVisibleActivity(thought: ThoughtItem, showThoughts: boolean) {
|
||||
return (
|
||||
(showThoughts && !!thought.thought?.trim()) || !!thought.tool || !!thought.message_files?.length
|
||||
)
|
||||
}
|
||||
|
||||
function getAgentActivityEntries(item: ChatItem): AgentActivityEntry[] {
|
||||
function getAgentActivityEntries(item: ChatItem, showThoughts: boolean): AgentActivityEntry[] {
|
||||
if (item.agent_response_parts?.length) {
|
||||
const keyOccurrences = new Map<string, number>()
|
||||
|
||||
@@ -128,7 +131,7 @@ function getAgentActivityEntries(item: ChatItem): AgentActivityEntry[] {
|
||||
if (part.type === 'message')
|
||||
return part.content ? [{ type: 'message', content: part.content, key }] : []
|
||||
|
||||
return hasVisibleActivity(part.thought)
|
||||
return hasVisibleActivity(part.thought, showThoughts)
|
||||
? [{ type: 'thought', thought: part.thought, key }]
|
||||
: []
|
||||
})
|
||||
@@ -142,7 +145,7 @@ function getAgentActivityEntries(item: ChatItem): AgentActivityEntry[] {
|
||||
const answer = thought.answer
|
||||
if (answer?.trim()) parts.push({ type: 'message', content: answer, key: `message-${key}` })
|
||||
|
||||
if (hasVisibleActivity(thought))
|
||||
if (hasVisibleActivity(thought, showThoughts))
|
||||
parts.push({ type: 'thought', thought, key: `thought-${key}` })
|
||||
|
||||
return parts
|
||||
@@ -218,7 +221,7 @@ function ToolActivityItem({ tool }: { tool: ToolActivity }) {
|
||||
<div className="flex w-full max-w-full min-w-0 flex-col items-start">
|
||||
{hasDetails ? (
|
||||
<Collapsible className="w-full max-w-full items-start">
|
||||
<CollapsibleTrigger className="group/tool flex h-6 min-h-0 max-w-full touch-manipulation items-center justify-start gap-0 rounded-md p-1 text-left system-xs-medium text-text-tertiary select-none hover:bg-state-base-hover hover:text-text-primary focus-visible:bg-state-base-hover data-panel-open:text-text-primary">
|
||||
<CollapsibleTrigger className="group/tool flex h-6 min-h-0 max-w-full touch-manipulation items-center justify-start gap-0 rounded-md p-1 text-start system-xs-medium text-text-tertiary select-none hover:bg-state-base-hover hover:text-text-primary focus-visible:bg-state-base-hover data-panel-open:text-text-primary">
|
||||
{content}
|
||||
</CollapsibleTrigger>
|
||||
<CollapsiblePanel className="w-full max-w-full">
|
||||
@@ -258,15 +261,22 @@ function ToolActivityItem({ tool }: { tool: ToolActivity }) {
|
||||
function AgentActivityItem({
|
||||
thought,
|
||||
responding,
|
||||
showThoughts,
|
||||
}: {
|
||||
thought: ThoughtItem
|
||||
responding?: boolean
|
||||
showThoughts: boolean
|
||||
}) {
|
||||
const locale = useLocale()
|
||||
const tools = getToolActivities(thought, locale, responding)
|
||||
|
||||
return (
|
||||
<div className="flex w-full max-w-full min-w-0 flex-col py-0.5">
|
||||
{showThoughts && !!thought.thought?.trim() && (
|
||||
<div className="px-1 body-md-regular wrap-break-word whitespace-pre-wrap text-text-secondary">
|
||||
{thought.thought.trim()}
|
||||
</div>
|
||||
)}
|
||||
{tools.map((tool) => (
|
||||
<ToolActivityItem key={`${getThoughtKey(thought)}-${tool.key}`} tool={tool} />
|
||||
))}
|
||||
@@ -288,11 +298,13 @@ function AgentActivityDisclosure({
|
||||
entries,
|
||||
responding,
|
||||
defaultOpen,
|
||||
showThoughts,
|
||||
}: {
|
||||
item: ChatItem
|
||||
entries: AgentActivityEntry[]
|
||||
responding?: boolean
|
||||
defaultOpen?: boolean
|
||||
showThoughts: boolean
|
||||
}) {
|
||||
const { t } = useTranslation(['agentV2'])
|
||||
const workingDuration = useWorkingDuration(responding)
|
||||
@@ -309,7 +321,7 @@ function AgentActivityDisclosure({
|
||||
<Collapsible className="w-full max-w-full items-start gap-1" defaultOpen={defaultOpen}>
|
||||
<CollapsibleTrigger
|
||||
aria-label={title}
|
||||
className="group/thinking flex h-6 min-h-0 max-w-full touch-manipulation items-center justify-start gap-1 rounded-md p-1 text-left system-xs-medium text-text-tertiary select-none hover:bg-state-base-hover hover:text-text-primary focus-visible:bg-state-base-hover data-panel-open:text-text-primary"
|
||||
className="group/thinking flex h-6 min-h-0 max-w-full touch-manipulation items-center justify-start gap-1 rounded-md p-1 text-start system-xs-medium text-text-tertiary select-none hover:bg-state-base-hover hover:text-text-primary focus-visible:bg-state-base-hover data-panel-open:text-text-primary"
|
||||
>
|
||||
<span>{thinking}</span>
|
||||
{duration && (
|
||||
@@ -332,7 +344,12 @@ function AgentActivityDisclosure({
|
||||
content={resolveKnowledgeCitationLinks(entry.content, item.citation)}
|
||||
/>
|
||||
) : (
|
||||
<AgentActivityItem key={entry.key} thought={entry.thought} responding={responding} />
|
||||
<AgentActivityItem
|
||||
key={entry.key}
|
||||
thought={entry.thought}
|
||||
responding={responding}
|
||||
showThoughts={showThoughts}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
@@ -345,6 +362,7 @@ export function AgentRosterResponseContent({
|
||||
item,
|
||||
responding,
|
||||
content,
|
||||
showThoughts = false,
|
||||
}: AgentRosterResponseContentProps) {
|
||||
if (item.annotation?.logAnnotation) {
|
||||
return (
|
||||
@@ -355,7 +373,7 @@ export function AgentRosterResponseContent({
|
||||
)
|
||||
}
|
||||
|
||||
const entries = getAgentActivityEntries(item)
|
||||
const entries = getAgentActivityEntries(item, showThoughts)
|
||||
const hasLiveResponseParts = !!item.agent_response_parts?.length
|
||||
const hasThinkingStatus =
|
||||
entries.length === 0 && !!item.agent_response_parts?.some((part) => part.type === 'thought')
|
||||
@@ -381,6 +399,7 @@ export function AgentRosterResponseContent({
|
||||
item={item}
|
||||
entries={entries}
|
||||
responding={responding}
|
||||
showThoughts={showThoughts}
|
||||
defaultOpen={hasLiveResponseParts && (!!responding || entries.length > 0)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -42,6 +42,7 @@ type AnswerProps = {
|
||||
item: ChatItem
|
||||
responding?: boolean
|
||||
content?: string
|
||||
showThoughts?: boolean
|
||||
}) => ReactNode
|
||||
onHumanInputFormSubmit?: (formToken: string, formData: HumanInputFormSubmitData) => Promise<void>
|
||||
}
|
||||
@@ -143,7 +144,12 @@ const Answer: FC<AnswerProps> = ({
|
||||
|
||||
const contentIsEmpty = typeof content === 'string' && content.trim() === ''
|
||||
const agentContentNode = renderAgentContent ? (
|
||||
renderAgentContent({ item, responding, content })
|
||||
renderAgentContent({
|
||||
item,
|
||||
responding,
|
||||
content,
|
||||
showThoughts: !!config?.show_thoughts?.enabled,
|
||||
})
|
||||
) : (
|
||||
<AgentContent item={item} responding={responding} content={content} />
|
||||
)
|
||||
@@ -163,21 +169,21 @@ const Answer: FC<AnswerProps> = ({
|
||||
<div className="relative size-10 shrink-0">
|
||||
{answerIcon || <AnswerIcon />}
|
||||
{responding && (
|
||||
<div className="absolute -top-0.75 -left-0.75 flex h-4 w-4 items-center rounded-full border-[0.5px] border-divider-subtle bg-background-section-burn pl-1.5 shadow-xs">
|
||||
<div className="absolute -inset-s-0.75 -top-0.75 flex h-4 w-4 items-center rounded-full border-[0.5px] border-divider-subtle bg-background-section-burn ps-1.5 shadow-xs">
|
||||
<LoadingAnim type="avatar" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="chat-answer-container group ml-4 w-0 grow pb-4"
|
||||
className="chat-answer-container group ms-4 w-0 grow pb-4"
|
||||
ref={containerRef}
|
||||
data-testid="chat-answer-container"
|
||||
>
|
||||
{/* Block 1: Workflow Process + Human Input Forms */}
|
||||
{hasHumanInputs && (
|
||||
<div
|
||||
className={cn('group relative pr-10', chatAnswerContainerInner)}
|
||||
className={cn('group relative pe-10', chatAnswerContainerInner)}
|
||||
data-testid="chat-answer-container-humaninput"
|
||||
>
|
||||
<div
|
||||
@@ -240,8 +246,8 @@ const Answer: FC<AnswerProps> = ({
|
||||
|
||||
{/* Block 2: Response Content (when human inputs exist) */}
|
||||
{hasHumanInputs && (responding || !contentIsEmpty || hasAgentContent || hasReasoning) && (
|
||||
<div className={cn('group relative mt-2 pr-10', chatAnswerContainerInner)}>
|
||||
<div className="absolute -top-2 left-6 h-3 w-0.5 bg-chat-answer-human-input-form-divider-bg" />
|
||||
<div className={cn('group relative mt-2 pe-10', chatAnswerContainerInner)}>
|
||||
<div className="absolute inset-s-6 -top-2 h-3 w-0.5 bg-chat-answer-human-input-form-divider-bg" />
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="relative inline-block w-full max-w-full rounded-2xl bg-chat-bubble-bg px-4 py-3 body-lg-regular text-text-primary"
|
||||
@@ -312,7 +318,7 @@ const Answer: FC<AnswerProps> = ({
|
||||
{/* Original single block layout (when no human inputs) */}
|
||||
{!hasHumanInputs && (
|
||||
<div
|
||||
className={cn('group relative pr-10', chatAnswerContainerInner)}
|
||||
className={cn('group relative pe-10', chatAnswerContainerInner)}
|
||||
data-testid="chat-answer-container-inner"
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -84,6 +84,7 @@ export type ChatProps = {
|
||||
item: ChatItem
|
||||
responding?: boolean
|
||||
content?: string
|
||||
showThoughts?: boolean
|
||||
}) => ReactNode
|
||||
onHumanInputFormSubmit?: (formToken: string, formData: HumanInputFormSubmitData) => Promise<void>
|
||||
getHumanInputNodeData?: (nodeID: string) => Node<HumanInputNodeType> | undefined
|
||||
|
||||
@@ -124,6 +124,13 @@ describe('NewFeaturePanel', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('does not offer thinking display in the shared application feature panel', () => {
|
||||
renderPanel({ isChatMode: true, inWorkflow: false })
|
||||
expect(
|
||||
screen.queryByRole('switch', { name: 'appDebug.feature.showThoughts.title' }),
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
describe('Chat Mode Features', () => {
|
||||
it('should render speech-to-text in chat mode when model is available', () => {
|
||||
renderPanel({ isChatMode: true })
|
||||
|
||||
@@ -15,6 +15,7 @@ import { FollowUp } from '@/app/components/base/features/new-feature-panel/follo
|
||||
import ImageUpload from '@/app/components/base/features/new-feature-panel/image-upload'
|
||||
import Moderation from '@/app/components/base/features/new-feature-panel/moderation'
|
||||
import MoreLikeThis from '@/app/components/base/features/new-feature-panel/more-like-this'
|
||||
import ShowThoughts from '@/app/components/base/features/new-feature-panel/show-thoughts'
|
||||
import SpeechToText from '@/app/components/base/features/new-feature-panel/speech-to-text'
|
||||
import TextToSpeech from '@/app/components/base/features/new-feature-panel/text-to-speech'
|
||||
import {
|
||||
@@ -33,6 +34,7 @@ type Props = Readonly<{
|
||||
onClose: () => void
|
||||
inWorkflow?: boolean
|
||||
showFileUpload?: boolean
|
||||
showThoughtsToggle?: boolean
|
||||
showModeration?: boolean
|
||||
showAnnotationReply?: boolean
|
||||
promptVariables?: PromptVariable[]
|
||||
@@ -51,6 +53,7 @@ const NewFeaturePanel = ({
|
||||
onClose,
|
||||
inWorkflow = true,
|
||||
showFileUpload = true,
|
||||
showThoughtsToggle = false,
|
||||
showModeration = true,
|
||||
showAnnotationReply = true,
|
||||
promptVariables,
|
||||
@@ -114,6 +117,9 @@ const NewFeaturePanel = ({
|
||||
{showFileUpload && isChatMode && <FileUpload disabled={disabled} onChange={onChange} />}
|
||||
{showFileUpload && !isChatMode && <ImageUpload disabled={disabled} onChange={onChange} />}
|
||||
{isChatMode && <Citation disabled={disabled} onChange={onChange} />}
|
||||
{isChatMode && showThoughtsToggle && (
|
||||
<ShowThoughts disabled={disabled} onChange={onChange} />
|
||||
)}
|
||||
{showModeration && (isChatMode || !inWorkflow) && (
|
||||
<Moderation disabled={disabled} onChange={onChange} />
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card'
|
||||
import { FeatureEnum } from '@/app/components/base/features/types'
|
||||
|
||||
type Props = Readonly<{
|
||||
disabled?: boolean
|
||||
onChange?: OnFeaturesChange
|
||||
}>
|
||||
|
||||
const ShowThoughts = ({ disabled, onChange }: Props) => {
|
||||
const { t } = useTranslation(['appDebug'])
|
||||
const features = useFeatures((s) => s.features)
|
||||
const featuresStore = useFeaturesStore()
|
||||
|
||||
const handleChange = useCallback(
|
||||
(type: FeatureEnum, enabled: boolean) => {
|
||||
const { features, setFeatures } = featuresStore!.getState()
|
||||
|
||||
const newFeatures = produce(features, (draft) => {
|
||||
draft[type] = {
|
||||
...draft[type],
|
||||
enabled,
|
||||
}
|
||||
})
|
||||
setFeatures(newFeatures)
|
||||
if (onChange) onChange(newFeatures)
|
||||
},
|
||||
[featuresStore, onChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<FeatureCard
|
||||
icon={
|
||||
<div className="shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-warning-warning-500 p-1 shadow-xs">
|
||||
<span
|
||||
aria-hidden
|
||||
className="i-ri-lightbulb-flash-line size-4 text-text-primary-on-surface"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
title={t(($) => $['feature.showThoughts.title'], { ns: 'appDebug' })}
|
||||
value={!!features.showThoughts?.enabled}
|
||||
description={t(($) => $['feature.showThoughts.description'], { ns: 'appDebug' })!}
|
||||
onChange={(state) => handleChange(FeatureEnum.showThoughts, state)}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default ShowThoughts
|
||||
@@ -40,6 +40,9 @@ export const createFeaturesStore = (initProps?: Partial<FeaturesState>) => {
|
||||
citation: {
|
||||
enabled: false,
|
||||
},
|
||||
showThoughts: {
|
||||
enabled: false,
|
||||
},
|
||||
moderation: {
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
@@ -84,6 +84,7 @@ export const FeatureEnum = {
|
||||
text2speech: 'text2speech',
|
||||
speech2text: 'speech2text',
|
||||
citation: 'citation',
|
||||
showThoughts: 'showThoughts',
|
||||
moderation: 'moderation',
|
||||
file: 'file',
|
||||
annotationReply: 'annotationReply',
|
||||
@@ -98,6 +99,7 @@ export type Features = {
|
||||
[FeatureEnum.text2speech]?: TextToSpeech
|
||||
[FeatureEnum.speech2text]?: EnabledOrDisabled
|
||||
[FeatureEnum.citation]?: EnabledOrDisabled
|
||||
[FeatureEnum.showThoughts]?: EnabledOrDisabled
|
||||
[FeatureEnum.moderation]?: SensitiveWordAvoidance
|
||||
[FeatureEnum.file]?: FileUpload
|
||||
[FeatureEnum.annotationReply]?: AnnotationReplyConfig
|
||||
|
||||
+30
@@ -58,6 +58,36 @@ function renderPanel(snapshot: AgentSoulAppFeaturesConfig, draft = snapshot, dis
|
||||
}
|
||||
|
||||
describe('AgentChatFeaturesPanel', () => {
|
||||
it('defaults thinking display off and saves toggles without changing citations', async () => {
|
||||
const user = userEvent.setup()
|
||||
const { store } = renderPanel({ retriever_resource: { enabled: true } })
|
||||
const toggle = screen.getByRole('switch', { name: 'appDebug.feature.showThoughts.title' })
|
||||
|
||||
expect(toggle).not.toBeChecked()
|
||||
await user.click(toggle)
|
||||
expect(toggle).toBeChecked()
|
||||
expect(store.get(agentComposerAppFeaturesAtom)?.show_thoughts).toEqual({ enabled: true })
|
||||
expect(store.get(agentComposerAppFeaturesAtom)?.retriever_resource).toEqual({ enabled: true })
|
||||
await user.click(toggle)
|
||||
expect(toggle).not.toBeChecked()
|
||||
expect(store.get(agentComposerAppFeaturesAtom)?.show_thoughts).toEqual({ enabled: false })
|
||||
})
|
||||
|
||||
it('shows the published thinking setting read-only without changing the draft', async () => {
|
||||
const user = userEvent.setup()
|
||||
const { store } = renderPanel(
|
||||
{ show_thoughts: { enabled: true } },
|
||||
{ show_thoughts: { enabled: false } },
|
||||
true,
|
||||
)
|
||||
const toggle = screen.getByRole('switch', { name: 'appDebug.feature.showThoughts.title' })
|
||||
|
||||
expect(toggle).toBeChecked()
|
||||
expect(toggle).toHaveAttribute('aria-disabled', 'true')
|
||||
await user.click(toggle)
|
||||
expect(store.get(agentComposerAppFeaturesAtom)?.show_thoughts).toEqual({ enabled: false })
|
||||
})
|
||||
|
||||
it('keeps a newly saved opener enabled before the server snapshot catches up', async () => {
|
||||
const user = userEvent.setup({ skipHover: true })
|
||||
const { updateSnapshot, store } = renderPanel({ opening_statement: '' })
|
||||
|
||||
+35
@@ -271,6 +271,41 @@ describe('Agent conversation history', () => {
|
||||
},
|
||||
)
|
||||
|
||||
it.each([false, true])(
|
||||
'uses the configured show thoughts setting (%s) in preview',
|
||||
async (enabled) => {
|
||||
messages.set('conversation-1', [
|
||||
createMessage({
|
||||
agent_thoughts: [
|
||||
{
|
||||
id: 'thought-setting',
|
||||
tool_labels: {},
|
||||
files: [],
|
||||
message_id: 'message-0',
|
||||
thought: 'Model reasoning from history',
|
||||
answer: 'Public progress',
|
||||
tool: '',
|
||||
tool_input: '',
|
||||
observation: '',
|
||||
position: 1,
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
renderChat({
|
||||
conversationId: 'conversation-1',
|
||||
agentSoulConfig: { schema_version: 1, app_features: { show_thoughts: { enabled } } },
|
||||
})
|
||||
const toggle = await screen.findByRole('button', {
|
||||
name: new RegExp(`${prefix}.answer.thinking`),
|
||||
})
|
||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
||||
await userEvent.setup().click(toggle)
|
||||
if (enabled) expect(await screen.findByText('Model reasoning from history')).toBeVisible()
|
||||
else expect(screen.queryByText('Model reasoning from history')).not.toBeInTheDocument()
|
||||
},
|
||||
)
|
||||
|
||||
it('shows historical thought answers and localized tool activity', async () => {
|
||||
const user = userEvent.setup()
|
||||
messages.set('conversation-1', [
|
||||
|
||||
@@ -222,6 +222,7 @@ export const buildChatConfig = ({
|
||||
more_like_this: { enabled: false },
|
||||
text_to_speech: toEnabledConfig(appFeatures.text_to_speech) as ChatConfig['text_to_speech'],
|
||||
speech_to_text: toEnabledConfig(appFeatures.speech_to_text),
|
||||
show_thoughts: toEnabledConfig(appFeatures.show_thoughts),
|
||||
retriever_resource: toEnabledConfig(appFeatures.retriever_resource),
|
||||
sensitive_word_avoidance: toEnabledConfig(appFeatures.sensitive_word_avoidance),
|
||||
annotation_reply: {
|
||||
|
||||
+4
@@ -31,6 +31,7 @@ const defaultFeatureState: Features = {
|
||||
text2speech: { enabled: false },
|
||||
speech2text: { enabled: false },
|
||||
citation: { enabled: false },
|
||||
showThoughts: { enabled: false },
|
||||
moderation: { enabled: false },
|
||||
file: {
|
||||
enabled: false,
|
||||
@@ -101,6 +102,7 @@ function toPanelFeatures(appFeatures?: AgentSoulAppFeaturesConfig): Features {
|
||||
defaultFeatureState.text2speech,
|
||||
speech2text: appFeatures?.speech_to_text ?? defaultFeatureState.speech2text,
|
||||
citation: appFeatures?.retriever_resource ?? defaultFeatureState.citation,
|
||||
showThoughts: appFeatures?.show_thoughts ?? defaultFeatureState.showThoughts,
|
||||
moderation:
|
||||
(appFeatures?.sensitive_word_avoidance as Features['moderation'] | undefined) ??
|
||||
defaultFeatureState.moderation,
|
||||
@@ -126,6 +128,7 @@ function toAppFeatures(
|
||||
text_to_speech: features.text2speech as AgentSoulAppFeaturesConfig['text_to_speech'],
|
||||
speech_to_text: features.speech2text,
|
||||
retriever_resource: features.citation,
|
||||
show_thoughts: features.showThoughts,
|
||||
file_upload: toAgentFileUploadFeatureConfig(features.file),
|
||||
}
|
||||
}
|
||||
@@ -156,6 +159,7 @@ function AgentChatFeaturesPanelContent({
|
||||
isChatMode
|
||||
disabled={!!disabled}
|
||||
inWorkflow={false}
|
||||
showThoughtsToggle
|
||||
showModeration={false}
|
||||
showAnnotationReply={false}
|
||||
drawerClassName="bg-components-panel-bg! data-[swipe-direction=right]:top-1! data-[swipe-direction=right]:right-0! data-[swipe-direction=right]:bottom-1! data-[swipe-direction=right]:rounded-r-none!"
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "توليد نصوص متعددة في وقت واحد، ثم تحريرها ومتابعة التوليد",
|
||||
"feature.moreLikeThis.tip": "سيؤدي استخدام هذه الميزة إلى تكبد نفقات إضافية للرموز",
|
||||
"feature.moreLikeThis.title": "المزيد مثل هذا",
|
||||
"feature.showThoughts.description": "اعرض محتوى تفكير النموذج في المحادثات.",
|
||||
"feature.showThoughts.title": "إظهار عملية التفكير",
|
||||
"feature.speechToText.description": "يمكن استخدام الإدخال الصوتي في الدردشة.",
|
||||
"feature.speechToText.title": "تحويل الكلام إلى نص",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "يمكن أن يعطي إعداد اقتراح الأسئلة التالية للمستخدمين دردشة أفضل.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Eyni anda bir neçə mətn yaradın, sonra redaktə edin və yaratmağa davam edin",
|
||||
"feature.moreLikeThis.tip": "Bu funksiyadan istifadə əlavə token sərfiyyatına səbəb olacaq",
|
||||
"feature.moreLikeThis.title": "Buna bənzər daha çox mətn",
|
||||
"feature.showThoughts.description": "Söhbətlərdə modelin düşüncə məzmununu göstərin.",
|
||||
"feature.showThoughts.title": "Düşüncə prosesini göstər",
|
||||
"feature.speechToText.description": "Söhbətdə səsli daxiletmədən istifadə etmək olar.",
|
||||
"feature.speechToText.title": "Səsin mətnə çevrilməsi",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Növbəti sual təkliflərinin qurulması istifadəçilərin söhbət təcrübəsini yaxşılaşdıra bilər.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Mehrere Texte gleichzeitig generieren und dann bearbeiten und weiter generieren",
|
||||
"feature.moreLikeThis.tip": "Die Verwendung dieser Funktion verursacht zusätzliche Token-Kosten",
|
||||
"feature.moreLikeThis.title": "Mehr davon",
|
||||
"feature.showThoughts.description": "Zeigt den Gedankengang des Modells in Gesprächen an.",
|
||||
"feature.showThoughts.title": "Gedankengang anzeigen",
|
||||
"feature.speechToText.description": "Einmal aktiviert, können Sie Spracheingabe verwenden.",
|
||||
"feature.speechToText.title": "Sprache zu Text",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Das Einrichten von Vorschlägen für nächste Fragen kann den Chat für Benutzer verbessern.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Generate multiple texts at once, and then edit and continue to generate",
|
||||
"feature.moreLikeThis.tip": "Using this feature will incur additional tokens overhead",
|
||||
"feature.moreLikeThis.title": "More like this",
|
||||
"feature.showThoughts.description": "Display the model’s thinking in conversations.",
|
||||
"feature.showThoughts.title": "Show thinking",
|
||||
"feature.speechToText.description": "Voice input can be used in chat.",
|
||||
"feature.speechToText.title": "Speech to Text",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Setting up next questions suggestion can give users a better chat.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Genera múltiples textos a la vez, luego edítalos y continúa generando",
|
||||
"feature.moreLikeThis.tip": "Usar esta función incurrirá en un costo adicional de tokens",
|
||||
"feature.moreLikeThis.title": "Más como esto",
|
||||
"feature.showThoughts.description": "Muestra el razonamiento del modelo en las conversaciones.",
|
||||
"feature.showThoughts.title": "Mostrar razonamiento",
|
||||
"feature.speechToText.description": "Una vez habilitado, puedes usar la entrada de voz.",
|
||||
"feature.speechToText.title": "Voz a Texto",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Configurar sugerencias de próximas preguntas puede proporcionar una mejor conversación.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "تولید چندین متن به طور همزمان، و سپس ویرایش و ادامه تولید",
|
||||
"feature.moreLikeThis.tip": "استفاده از این ویژگی هزینههای اضافی توکنها را به همراه دارد",
|
||||
"feature.moreLikeThis.title": "بیشتر از این",
|
||||
"feature.showThoughts.description": "محتوای تفکر مدل را در گفتگوها نمایش دهید.",
|
||||
"feature.showThoughts.title": "نمایش روند تفکر",
|
||||
"feature.speechToText.description": "پس از فعال شدن، میتوانید از ورودی صوتی استفاده کنید.",
|
||||
"feature.speechToText.title": "تبدیل گفتار به متن",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "تنظیم پیشنهاد سوالات بعدی میتواند به کاربران یک چت بهتر ارائه دهد.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Générez plusieurs textes à la fois, puis modifiez et continuez à générer",
|
||||
"feature.moreLikeThis.tip": "L'utilisation de cette fonctionnalité entraînera un surcoût de tokens supplémentaires",
|
||||
"feature.moreLikeThis.title": "Plus comme ça",
|
||||
"feature.showThoughts.description": "Affichez le raisonnement du modèle dans les conversations.",
|
||||
"feature.showThoughts.title": "Afficher le raisonnement",
|
||||
"feature.speechToText.description": "Une fois activé, vous pouvez utiliser l'entrée vocale.",
|
||||
"feature.speechToText.title": "Discours en Texte",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "La configuration de la suggestion des prochaines questions peut offrir aux utilisateurs une meilleure discussion.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "एक बार में कई पाठ उत्पन्न करें, और फिर संपादित करें और जारी रखें",
|
||||
"feature.moreLikeThis.tip": "इस सुविधा का उपयोग करने से अतिरिक्त टोकन खर्च होगा",
|
||||
"feature.moreLikeThis.title": "ऐसा और",
|
||||
"feature.showThoughts.description": "बातचीत में मॉडल की सोचने की प्रक्रिया दिखाएँ।",
|
||||
"feature.showThoughts.title": "सोचने की प्रक्रिया दिखाएँ",
|
||||
"feature.speechToText.description": "सक्रिय होने पर, आप वॉयस इनपुट का उपयोग कर सकते हैं।",
|
||||
"feature.speechToText.title": "वाक् से पाठ",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "अगले प्रश्न सुझाव सेट करना उपयोगकर्ताओं को बेहतर चैट दे सकता है।",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Buat beberapa teks sekaligus, lalu edit dan lanjutkan membuat",
|
||||
"feature.moreLikeThis.tip": "Menggunakan fitur ini akan dikenakan overhead token tambahan",
|
||||
"feature.moreLikeThis.title": "Lebih seperti ini",
|
||||
"feature.showThoughts.description": "Tampilkan isi pemikiran model dalam percakapan.",
|
||||
"feature.showThoughts.title": "Tampilkan proses berpikir",
|
||||
"feature.speechToText.description": "Input suara dapat digunakan dalam obrolan.",
|
||||
"feature.speechToText.title": "Ucapan ke Teks",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Menyiapkan saran pertanyaan berikutnya dapat memberi pengguna obrolan yang lebih baik.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Genera più testi contemporaneamente, poi modifica e continua a generare",
|
||||
"feature.moreLikeThis.tip": "L'utilizzo di questa funzione comporterà un costo aggiuntivo di token",
|
||||
"feature.moreLikeThis.title": "Altri simili",
|
||||
"feature.showThoughts.description": "Mostra il ragionamento del modello nelle conversazioni.",
|
||||
"feature.showThoughts.title": "Mostra ragionamento",
|
||||
"feature.speechToText.description": "Una volta abilitato, puoi usare l'input vocale.",
|
||||
"feature.speechToText.title": "Da voce a testo",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Impostare suggerimenti per le prossime domande può offrire agli utenti una chat migliore.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "一度に複数のテキストを生成し、編集して生成を続ける",
|
||||
"feature.moreLikeThis.tip": "この機能を使用すると、追加のトークンオーバーヘッドが発生します",
|
||||
"feature.moreLikeThis.title": "これに似たもの",
|
||||
"feature.showThoughts.description": "会話中にモデルの思考内容を表示します。",
|
||||
"feature.showThoughts.title": "思考過程を表示",
|
||||
"feature.speechToText.description": "有効にすると、音声入力を使用できます。",
|
||||
"feature.speechToText.title": "音声からテキストへ",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "次の質問の提案を設定すると、ユーザーにより良いチャットが提供されます。",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "여러 텍스트를 한 번에 생성하고 편집하여 계속해서 생성합니다.",
|
||||
"feature.moreLikeThis.tip": "이 기능을 사용하면 추가적인 토큰 오버헤드가 발생합니다",
|
||||
"feature.moreLikeThis.title": "유사한 항목",
|
||||
"feature.showThoughts.description": "대화에서 모델의 사고 내용을 표시합니다.",
|
||||
"feature.showThoughts.title": "사고 과정 표시",
|
||||
"feature.speechToText.description": "활성화하면 음성 입력을 사용할 수 있습니다.",
|
||||
"feature.speechToText.title": "음성에서 텍스트로",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "다음 질문 제안을 설정하면 사용자에게 더 나은 채팅이 제공됩니다.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "ສ້າງຫຼາຍຂໍ້ຄວາມພ້ອມກັນ, ຈາກນັ້ນແກ້ໄຂ ແລະ ສ້າງຕໍ່",
|
||||
"feature.moreLikeThis.tip": "ການໃຊ້ຄຸນສົມບັດນີ້ຈະມີຄ່າໃຊ້ຈ່າຍ tokens ເພີ່ມເຕີມ",
|
||||
"feature.moreLikeThis.title": "ຂໍ້ຄວາມທີ່ຄ້າຍຄືກັນນີ້",
|
||||
"feature.showThoughts.description": "ສະແດງເນື້ອຫາການຄິດຂອງໂມເດລໃນການສົນທະນາ.",
|
||||
"feature.showThoughts.title": "ສະແດງຂະບວນການຄິດ",
|
||||
"feature.speechToText.description": "ສາມາດໃຊ້ການປ້ອນຂໍ້ມູນດ້ວຍສຽງໃນການສົນທະນາໄດ້.",
|
||||
"feature.speechToText.title": "ປ່ຽນສຽງເປັນຂໍ້ຄວາມ",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "ການຕັ້ງຄ່າຄຳຖາມແນະນຳຖັດໄປຈະຊ່ວຍໃຫ້ຜູ້ໃຊ້ມີການສົນທະນາທີ່ດີຂຶ້ນ.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Generate multiple texts at once, and then edit and continue to generate",
|
||||
"feature.moreLikeThis.tip": "Using this feature will incur additional tokens overhead",
|
||||
"feature.moreLikeThis.title": "More like this",
|
||||
"feature.showThoughts.description": "Toon het denkproces van het model in gesprekken.",
|
||||
"feature.showThoughts.title": "Denkproces tonen",
|
||||
"feature.speechToText.description": "Voice input can be used in chat.",
|
||||
"feature.speechToText.title": "Speech to Text",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Setting up next questions suggestion can give users a better chat.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Generuj wiele tekstów na raz, a następnie edytuj i kontynuuj generowanie",
|
||||
"feature.moreLikeThis.tip": "Korzystanie z tej funkcji spowoduje dodatkowe zużycie tokenów",
|
||||
"feature.moreLikeThis.title": "Więcej takich jak ten",
|
||||
"feature.showThoughts.description": "Wyświetlaj tok rozumowania modelu w rozmowach.",
|
||||
"feature.showThoughts.title": "Pokaż tok rozumowania",
|
||||
"feature.speechToText.description": "Po włączeniu można używać wprowadzania głosowego.",
|
||||
"feature.speechToText.title": "Mowa na tekst",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Ustawienie kolejnych pytań może poprawić czat.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Gere vários textos de uma vez e, em seguida, edite e continue a gerar",
|
||||
"feature.moreLikeThis.tip": "Usar esse recurso incorrerá em sobrecarga adicional de tokens",
|
||||
"feature.moreLikeThis.title": "Mais como isso",
|
||||
"feature.showThoughts.description": "Exiba o raciocínio do modelo nas conversas.",
|
||||
"feature.showThoughts.title": "Mostrar raciocínio",
|
||||
"feature.speechToText.description": "Uma vez ativado, você pode usar entrada de voz.",
|
||||
"feature.speechToText.title": "Fala para Texto",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Configurar sugestões de próximas perguntas pode proporcionar um melhor chat aos usuários.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Generați mai multe texte o dată, apoi editați și continuați să generați",
|
||||
"feature.moreLikeThis.tip": "Utilizarea acestei funcții va genera un consum suplimentar de jetoane",
|
||||
"feature.moreLikeThis.title": "Mai multe ca aceasta",
|
||||
"feature.showThoughts.description": "Afișează raționamentul modelului în conversații.",
|
||||
"feature.showThoughts.title": "Afișează raționamentul",
|
||||
"feature.speechToText.description": "După activare, puteți utiliza intrarea vocală.",
|
||||
"feature.speechToText.title": "Voce la text",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Setarea sugestiilor pentru întrebările următoare poate oferi utilizatorilor o conversație mai bună.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Сгенерируйте несколько текстов одновременно, а затем отредактируйте и продолжайте генерировать",
|
||||
"feature.moreLikeThis.tip": "Использование этой функции приведет к дополнительным расходам токенов",
|
||||
"feature.moreLikeThis.title": "Больше похожего",
|
||||
"feature.showThoughts.description": "Показывать рассуждения модели в диалогах.",
|
||||
"feature.showThoughts.title": "Показывать рассуждения",
|
||||
"feature.speechToText.description": "После включения вы можете использовать голосовой ввод.",
|
||||
"feature.speechToText.title": "Преобразование речи в текст",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Настройка предложения следующих вопросов может улучшить чат для пользователей.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Ustvari več besedil naenkrat, nato pa jih urejaj in nadaljuj z ustvarjanjem.",
|
||||
"feature.moreLikeThis.tip": "Uporaba te funkcije povzroča dodatno porabo žetonov.",
|
||||
"feature.moreLikeThis.title": "Več takšnih",
|
||||
"feature.showThoughts.description": "Prikaži razmišljanje modela v pogovorih.",
|
||||
"feature.showThoughts.title": "Prikaži razmišljanje",
|
||||
"feature.speechToText.description": "Ko je omogočeno, lahko uporabljaš glasovni vnos.",
|
||||
"feature.speechToText.title": "Govor v besedilo",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Nastavitev predlogov za naslednja vprašanja lahko uporabnikom izboljša klepet.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "สร้างข้อความหลายข้อความพร้อมกัน จากนั้นแก้ไขและสร้างต่อไป",
|
||||
"feature.moreLikeThis.tip": "การใช้คุณสมบัตินี้จะมีค่าใช้จ่ายโทเค็นเพิ่มเติม",
|
||||
"feature.moreLikeThis.title": "เพิ่มเติมเช่นนี้",
|
||||
"feature.showThoughts.description": "แสดงเนื้อหาการคิดของโมเดลในการสนทนา",
|
||||
"feature.showThoughts.title": "แสดงกระบวนการคิด",
|
||||
"feature.speechToText.description": "สามารถใช้การป้อนข้อมูลด้วยเสียงในการแชทได้",
|
||||
"feature.speechToText.title": "คําพูดเป็นข้อความ",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "การตั้งค่าคําแนะนําคําถามถัดไปจะช่วยให้ผู้ใช้แชทได้ดีขึ้น",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Birden fazla metni bir kerede üretin, ardından düzenleyin ve üretmeye devam edin",
|
||||
"feature.moreLikeThis.tip": "Bu özelliği kullanmak ek token maliyetine yol açacaktır",
|
||||
"feature.moreLikeThis.title": "Bunun gibi daha fazlası",
|
||||
"feature.showThoughts.description": "Sohbetlerde modelin düşünme içeriğini gösterir.",
|
||||
"feature.showThoughts.title": "Düşünme sürecini göster",
|
||||
"feature.speechToText.description": "Etkinleştirildiğinde, sesli giriş kullanabilirsiniz.",
|
||||
"feature.speechToText.title": "Sesten Metne",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Sonraki soru önerilerini ayarlamak, kullanıcılara daha iyi bir sohbet deneyimi sunabilir.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Згенерувати кілька текстів одночасно, а потім редагувати та продовжити генерацію",
|
||||
"feature.moreLikeThis.tip": "Використання цієї функції призведе до додаткових витрат токенів",
|
||||
"feature.moreLikeThis.title": "Більше такого",
|
||||
"feature.showThoughts.description": "Показувати міркування моделі в розмовах.",
|
||||
"feature.showThoughts.title": "Показувати міркування",
|
||||
"feature.speechToText.description": "Після увімкнення ви можете використовувати голосовий ввід.",
|
||||
"feature.speechToText.title": "Мовлення в текст",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Налаштування пропозицій наступних запитань може надати користувачам кращий чат.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "Tạo nhiều văn bản cùng một lúc, sau đó chỉnh sửa và tiếp tục tạo ra.",
|
||||
"feature.moreLikeThis.tip": "Sử dụng tính năng này sẽ tiêu tốn thêm token.",
|
||||
"feature.moreLikeThis.title": "Thêm tương tự",
|
||||
"feature.showThoughts.description": "Hiển thị nội dung suy nghĩ của mô hình trong cuộc trò chuyện.",
|
||||
"feature.showThoughts.title": "Hiển thị quá trình suy nghĩ",
|
||||
"feature.speechToText.description": "Khi được bật, bạn có thể sử dụng đầu vào bằng giọng nói.",
|
||||
"feature.speechToText.title": "Chuyển đổi giọng nói thành văn bản",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "Thiết lập đề xuất câu hỏi tiếp theo có thể tạo ra cuộc trò chuyện tốt hơn cho người dùng.",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "一次生成多条文本,可在此基础上编辑并继续生成",
|
||||
"feature.moreLikeThis.tip": "使用此功能将会额外消耗 tokens",
|
||||
"feature.moreLikeThis.title": "更多类似的",
|
||||
"feature.showThoughts.description": "在对话中展示模型的思考内容。",
|
||||
"feature.showThoughts.title": "显示思考过程",
|
||||
"feature.speechToText.description": "您可以使用语音输入。",
|
||||
"feature.speechToText.title": "语音转文字",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "设置下一步问题建议可以让用户更好的对话。",
|
||||
|
||||
@@ -95,6 +95,8 @@
|
||||
"feature.moreLikeThis.description": "一次生成多條文字,可在此基礎上編輯並繼續生成",
|
||||
"feature.moreLikeThis.tip": "使用此功能將會額外消耗 tokens",
|
||||
"feature.moreLikeThis.title": "更多類似的",
|
||||
"feature.showThoughts.description": "在對話中展示模型的思考內容。",
|
||||
"feature.showThoughts.title": "顯示思考過程",
|
||||
"feature.speechToText.description": "啟用後,您可以使用語音輸入。",
|
||||
"feature.speechToText.title": "語音轉文字",
|
||||
"feature.suggestedQuestionsAfterAnswer.description": "設定下一步問題建議可以讓使用者更好的對話。",
|
||||
|
||||
Vendored
+2
@@ -1091,6 +1091,8 @@ export default interface Resources {
|
||||
'feature.moreLikeThis.description': 'Generate multiple texts at once, and then edit and continue to generate'
|
||||
'feature.moreLikeThis.tip': 'Using this feature will incur additional tokens overhead'
|
||||
'feature.moreLikeThis.title': 'More like this'
|
||||
'feature.showThoughts.description': 'Display the model’s thinking in conversations.'
|
||||
'feature.showThoughts.title': 'Show thinking'
|
||||
'feature.speechToText.description': 'Voice input can be used in chat.'
|
||||
'feature.speechToText.title': 'Speech to Text'
|
||||
'feature.suggestedQuestionsAfterAnswer.description': 'Setting up next questions suggestion can give users a better chat.'
|
||||
|
||||
@@ -261,6 +261,9 @@ export type ModelConfig = {
|
||||
language?: string
|
||||
autoPlay?: TtsAutoPlay
|
||||
}
|
||||
show_thoughts?: {
|
||||
enabled: boolean
|
||||
}
|
||||
retriever_resource: {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user