streamlit-coco 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- streamlit_coco/__init__.py +115 -0
- streamlit_coco/ask_user.py +120 -0
- streamlit_coco/bootstrap.py +198 -0
- streamlit_coco/bridge.py +42 -0
- streamlit_coco/component.py +336 -0
- streamlit_coco/debug.py +35 -0
- streamlit_coco/diagnostics.py +127 -0
- streamlit_coco/display.py +232 -0
- streamlit_coco/errors.py +120 -0
- streamlit_coco/frontend/__init__.py +1 -0
- streamlit_coco/frontend/index.html +1 -0
- streamlit_coco/frontend/main.js +565 -0
- streamlit_coco/frontend/style.css +224 -0
- streamlit_coco/messages.py +527 -0
- streamlit_coco/options.py +107 -0
- streamlit_coco/permissions.py +258 -0
- streamlit_coco/query.py +39 -0
- streamlit_coco/session.py +498 -0
- streamlit_coco/sql_tool.py +130 -0
- streamlit_coco/text_renderer.py +40 -0
- streamlit_coco/tool_cards.py +490 -0
- streamlit_coco/tool_extract.py +167 -0
- streamlit_coco/tool_names.py +86 -0
- streamlit_coco/ui.py +642 -0
- streamlit_coco-0.1.0.dist-info/METADATA +213 -0
- streamlit_coco-0.1.0.dist-info/RECORD +28 -0
- streamlit_coco-0.1.0.dist-info/WHEEL +4 -0
- streamlit_coco-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Streamlit component and Python library for Snowflake CoCo.
|
|
2
|
+
|
|
3
|
+
Core / headless symbols import without loading Streamlit. UI helpers
|
|
4
|
+
(``panel``, ``chat``, …) are resolved lazily on first access.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from importlib import import_module
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from streamlit_coco.ask_user import is_ask_user_question
|
|
13
|
+
from streamlit_coco.debug import is_debug_mode
|
|
14
|
+
from streamlit_coco.diagnostics import CocoEnvironment, check_environment, require_environment
|
|
15
|
+
from streamlit_coco.errors import (
|
|
16
|
+
ApprovalTimeoutError,
|
|
17
|
+
CLINotFoundError,
|
|
18
|
+
CLIProbeError,
|
|
19
|
+
CocoConnectionError,
|
|
20
|
+
CocoError,
|
|
21
|
+
QueryError,
|
|
22
|
+
SDKNotInstalledError,
|
|
23
|
+
SessionNotReadyError,
|
|
24
|
+
SessionStartError,
|
|
25
|
+
SnowflakeConfigNotFoundError,
|
|
26
|
+
)
|
|
27
|
+
from streamlit_coco.messages import CocoEvent, events_to_dataframe
|
|
28
|
+
from streamlit_coco.options import CocoOptions
|
|
29
|
+
from streamlit_coco.permissions import approve_pending, deny_pending
|
|
30
|
+
from streamlit_coco.query import query
|
|
31
|
+
from streamlit_coco.session import CocoChatResult, CocoRunStatus, CocoSession, get_session
|
|
32
|
+
from streamlit_coco.tool_names import is_exit_plan_mode, is_sql_tool, tool_family
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
# UI / Streamlit-backed exports — loaded on first attribute access.
|
|
37
|
+
_LAZY_ATTRS: dict[str, tuple[str, str]] = {
|
|
38
|
+
"chat": ("streamlit_coco.component", "chat"),
|
|
39
|
+
"request_input": ("streamlit_coco.ui", "request_input"),
|
|
40
|
+
"chat_input_bar": ("streamlit_coco.bootstrap", "chat_input_bar"),
|
|
41
|
+
"get_or_create_session": ("streamlit_coco.bootstrap", "get_or_create_session"),
|
|
42
|
+
"render_environment_status": ("streamlit_coco.bootstrap", "render_environment_status"),
|
|
43
|
+
"render_start_gate": ("streamlit_coco.bootstrap", "render_start_gate"),
|
|
44
|
+
"reset_session": ("streamlit_coco.bootstrap", "reset_session"),
|
|
45
|
+
"stop_session": ("streamlit_coco.bootstrap", "stop_session"),
|
|
46
|
+
"get_latest_assistant_text": ("streamlit_coco.display", "get_latest_assistant_text"),
|
|
47
|
+
"render_output_field": ("streamlit_coco.display", "render_output_field"),
|
|
48
|
+
"render_session_status": ("streamlit_coco.display", "render_session_status"),
|
|
49
|
+
"render_transcript": ("streamlit_coco.display", "render_transcript"),
|
|
50
|
+
"panel": ("streamlit_coco.ui", "panel"),
|
|
51
|
+
"render_approvals": ("streamlit_coco.ui", "render_approvals"),
|
|
52
|
+
"render_plan_banner": ("streamlit_coco.ui", "render_plan_banner"),
|
|
53
|
+
"send_prompt": ("streamlit_coco.ui", "send_prompt"),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"ApprovalTimeoutError",
|
|
58
|
+
"CLIProbeError",
|
|
59
|
+
"CLINotFoundError",
|
|
60
|
+
"CocoChatResult",
|
|
61
|
+
"CocoConnectionError",
|
|
62
|
+
"CocoEnvironment",
|
|
63
|
+
"CocoError",
|
|
64
|
+
"CocoEvent",
|
|
65
|
+
"CocoOptions",
|
|
66
|
+
"CocoRunStatus",
|
|
67
|
+
"CocoSession",
|
|
68
|
+
"QueryError",
|
|
69
|
+
"SDKNotInstalledError",
|
|
70
|
+
"SessionNotReadyError",
|
|
71
|
+
"SessionStartError",
|
|
72
|
+
"SnowflakeConfigNotFoundError",
|
|
73
|
+
"approve_pending",
|
|
74
|
+
"chat",
|
|
75
|
+
"chat_input_bar",
|
|
76
|
+
"check_environment",
|
|
77
|
+
"deny_pending",
|
|
78
|
+
"events_to_dataframe",
|
|
79
|
+
"get_or_create_session",
|
|
80
|
+
"get_session",
|
|
81
|
+
"get_latest_assistant_text",
|
|
82
|
+
"is_ask_user_question",
|
|
83
|
+
"is_debug_mode",
|
|
84
|
+
"is_exit_plan_mode",
|
|
85
|
+
"is_sql_tool",
|
|
86
|
+
"panel",
|
|
87
|
+
"query",
|
|
88
|
+
"render_approvals",
|
|
89
|
+
"render_plan_banner",
|
|
90
|
+
"require_environment",
|
|
91
|
+
"render_environment_status",
|
|
92
|
+
"render_output_field",
|
|
93
|
+
"render_session_status",
|
|
94
|
+
"render_start_gate",
|
|
95
|
+
"render_transcript",
|
|
96
|
+
"request_input",
|
|
97
|
+
"reset_session",
|
|
98
|
+
"send_prompt",
|
|
99
|
+
"stop_session",
|
|
100
|
+
"tool_family",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def __getattr__(name: str) -> Any:
|
|
105
|
+
target = _LAZY_ATTRS.get(name)
|
|
106
|
+
if target is None:
|
|
107
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
108
|
+
module_name, attr = target
|
|
109
|
+
value = getattr(import_module(module_name), attr)
|
|
110
|
+
globals()[name] = value
|
|
111
|
+
return value
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def __dir__() -> list[str]:
|
|
115
|
+
return sorted(set(__all__) | set(globals()) | {"__version__"})
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Helpers for CoCo AskUserQuestion tool interactions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from streamlit_coco.tool_names import is_ask_user_question, normalize_tool_name
|
|
8
|
+
|
|
9
|
+
OTHER_OPTION_LABEL = "Other..."
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"OTHER_OPTION_LABEL",
|
|
13
|
+
"build_answers_payload",
|
|
14
|
+
"choice_labels_with_other",
|
|
15
|
+
"extract_questions",
|
|
16
|
+
"format_option_label",
|
|
17
|
+
"is_ask_user_question",
|
|
18
|
+
"is_other_choice",
|
|
19
|
+
"normalize_tool_name",
|
|
20
|
+
"option_is_free_form",
|
|
21
|
+
"options_already_include_other",
|
|
22
|
+
"resolve_selected_labels",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def extract_questions(tool_input: dict[str, Any] | None) -> list[dict[str, Any]]:
|
|
27
|
+
"""Return the questions list from an AskUserQuestion tool input."""
|
|
28
|
+
if not isinstance(tool_input, dict):
|
|
29
|
+
return []
|
|
30
|
+
questions = tool_input.get("questions")
|
|
31
|
+
if not isinstance(questions, list):
|
|
32
|
+
return []
|
|
33
|
+
return [q for q in questions if isinstance(q, dict)]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def build_answers_payload(
|
|
37
|
+
questions: list[dict[str, Any]],
|
|
38
|
+
answers: dict[str, str],
|
|
39
|
+
) -> dict[str, Any]:
|
|
40
|
+
"""Build the ``updated_input`` payload expected by the CoCo SDK."""
|
|
41
|
+
return {"questions": questions, "answers": answers}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def format_option_label(option: dict[str, Any]) -> str:
|
|
45
|
+
"""Human-readable label for a question option."""
|
|
46
|
+
label = str(option.get("label") or "").strip() or "Option"
|
|
47
|
+
description = str(option.get("description") or "").strip()
|
|
48
|
+
if description:
|
|
49
|
+
return f"{label} — {description}"
|
|
50
|
+
return label
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def option_is_free_form(option: dict[str, Any]) -> bool:
|
|
54
|
+
return bool(option.get("freeForm") or option.get("free_form"))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def options_already_include_other(options: list[dict[str, Any]]) -> bool:
|
|
58
|
+
"""True when the agent already provided an Other / free-form style choice."""
|
|
59
|
+
return any(_is_other_style_option(opt) for opt in options)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _is_other_style_option(option: dict[str, Any]) -> bool:
|
|
63
|
+
"""True for free-form / Other / Something else choices that belong at the end."""
|
|
64
|
+
if option_is_free_form(option):
|
|
65
|
+
return True
|
|
66
|
+
label = str(option.get("label") or "").strip().lower().rstrip(".")
|
|
67
|
+
return label in {
|
|
68
|
+
"other",
|
|
69
|
+
"other...",
|
|
70
|
+
"something else",
|
|
71
|
+
"none of the above",
|
|
72
|
+
"type your own",
|
|
73
|
+
"type your own feedback",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def choice_labels_with_other(options: list[dict[str, Any]]) -> list[str]:
|
|
78
|
+
"""Display labels for widgets; free-form / Other choices always last."""
|
|
79
|
+
primary: list[str] = []
|
|
80
|
+
other_style: list[str] = []
|
|
81
|
+
for opt in options:
|
|
82
|
+
label = format_option_label(opt)
|
|
83
|
+
if _is_other_style_option(opt):
|
|
84
|
+
other_style.append(label)
|
|
85
|
+
else:
|
|
86
|
+
primary.append(label)
|
|
87
|
+
labels = primary + other_style
|
|
88
|
+
if options and not other_style:
|
|
89
|
+
labels.append(OTHER_OPTION_LABEL)
|
|
90
|
+
return labels
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def is_other_choice(value: str | None) -> bool:
|
|
94
|
+
if value is None:
|
|
95
|
+
return False
|
|
96
|
+
stripped = value.strip().lower().rstrip(".")
|
|
97
|
+
return stripped == "other" or value == OTHER_OPTION_LABEL
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def resolve_selected_labels(
|
|
101
|
+
options: list[dict[str, Any]],
|
|
102
|
+
selected: list[str] | str | None,
|
|
103
|
+
) -> list[str]:
|
|
104
|
+
"""Map widget selection(s) back to canonical option labels."""
|
|
105
|
+
if selected is None:
|
|
106
|
+
return []
|
|
107
|
+
values = [selected] if isinstance(selected, str) else list(selected)
|
|
108
|
+
labels_by_display = {format_option_label(opt): str(opt.get("label") or "") for opt in options}
|
|
109
|
+
labels_by_label = {str(opt.get("label") or ""): str(opt.get("label") or "") for opt in options}
|
|
110
|
+
resolved: list[str] = []
|
|
111
|
+
for value in values:
|
|
112
|
+
if is_other_choice(value):
|
|
113
|
+
resolved.append(OTHER_OPTION_LABEL)
|
|
114
|
+
elif value in labels_by_display:
|
|
115
|
+
resolved.append(labels_by_display[value])
|
|
116
|
+
elif value in labels_by_label:
|
|
117
|
+
resolved.append(labels_by_label[value])
|
|
118
|
+
elif value:
|
|
119
|
+
resolved.append(str(value))
|
|
120
|
+
return [label for label in resolved if label]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""High-level Streamlit helpers to keep app code small."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import streamlit as st
|
|
9
|
+
|
|
10
|
+
from streamlit_coco.diagnostics import CocoEnvironment, check_environment
|
|
11
|
+
from streamlit_coco.options import CocoOptions
|
|
12
|
+
from streamlit_coco.session import CocoRunStatus, CocoSession
|
|
13
|
+
from streamlit_coco.ui import send_prompt
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def render_environment_status(
|
|
17
|
+
env: CocoEnvironment | None = None,
|
|
18
|
+
*,
|
|
19
|
+
connection: str | None = None,
|
|
20
|
+
stacked: bool = False,
|
|
21
|
+
show_title: bool = True,
|
|
22
|
+
) -> CocoEnvironment:
|
|
23
|
+
"""Render SDK / CLI / Snowflake readiness. Probes the environment when ``env`` is omitted."""
|
|
24
|
+
status = env or check_environment(connection=connection)
|
|
25
|
+
if show_title:
|
|
26
|
+
st.subheader("CoCo environment")
|
|
27
|
+
|
|
28
|
+
def _sdk_block() -> None:
|
|
29
|
+
if status.sdk_installed:
|
|
30
|
+
st.success(f"SDK installed · `{status.sdk_version or 'unknown'}`")
|
|
31
|
+
else:
|
|
32
|
+
st.error("SDK missing — `pip install cortex-code-agent-sdk`")
|
|
33
|
+
|
|
34
|
+
def _cli_block() -> None:
|
|
35
|
+
if status.cli_ok:
|
|
36
|
+
st.success(f"CLI · `{status.cli_version}`")
|
|
37
|
+
if status.cli_path:
|
|
38
|
+
st.caption(status.cli_path)
|
|
39
|
+
elif status.cli_path:
|
|
40
|
+
st.warning("CLI found but `--version` failed")
|
|
41
|
+
st.caption(status.cli_path)
|
|
42
|
+
else:
|
|
43
|
+
st.error("CLI not on PATH (`cortex`)")
|
|
44
|
+
|
|
45
|
+
def _snowflake_block() -> None:
|
|
46
|
+
display = status.snowflake_config_display
|
|
47
|
+
if display:
|
|
48
|
+
# Single line with the path (filename appears once inside it).
|
|
49
|
+
st.success(f"Snowflake config · `{display}`")
|
|
50
|
+
st.caption(f"Connection: `{status.connection_hint}`")
|
|
51
|
+
else:
|
|
52
|
+
st.warning("No `~/.snowflake/connections.toml` (or config.toml)")
|
|
53
|
+
|
|
54
|
+
if stacked:
|
|
55
|
+
_sdk_block()
|
|
56
|
+
_cli_block()
|
|
57
|
+
_snowflake_block()
|
|
58
|
+
else:
|
|
59
|
+
c1, c2, c3 = st.columns(3)
|
|
60
|
+
with c1:
|
|
61
|
+
_sdk_block()
|
|
62
|
+
with c2:
|
|
63
|
+
_cli_block()
|
|
64
|
+
with c3:
|
|
65
|
+
_snowflake_block()
|
|
66
|
+
|
|
67
|
+
return status
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_or_create_session(
|
|
71
|
+
options: CocoOptions,
|
|
72
|
+
*,
|
|
73
|
+
key: str = "coco",
|
|
74
|
+
sync_options: bool = True,
|
|
75
|
+
) -> CocoSession:
|
|
76
|
+
"""Return a ``CocoSession`` stored in ``st.session_state[key]``."""
|
|
77
|
+
session = st.session_state.get(key)
|
|
78
|
+
if session is None or not isinstance(session, CocoSession):
|
|
79
|
+
session = CocoSession(options=options, key=key)
|
|
80
|
+
st.session_state[key] = session
|
|
81
|
+
elif sync_options:
|
|
82
|
+
session.sync_options(options)
|
|
83
|
+
return session
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def render_start_gate(
|
|
87
|
+
options: CocoOptions,
|
|
88
|
+
*,
|
|
89
|
+
session_key: str = "coco",
|
|
90
|
+
gate_key: str = "coco_started",
|
|
91
|
+
title: str = "CoCo demo is ready to be instantiated",
|
|
92
|
+
button_label: str = "Start CoCo Chat",
|
|
93
|
+
body: str | None = None,
|
|
94
|
+
warm_up: bool = True,
|
|
95
|
+
env: CocoEnvironment | None = None,
|
|
96
|
+
) -> bool:
|
|
97
|
+
"""Landing screen with environment probe and a start button.
|
|
98
|
+
|
|
99
|
+
Returns ``True`` when the user has started CoCo (caller continues).
|
|
100
|
+
Returns ``False`` while still on the gate (caller should ``st.stop()``).
|
|
101
|
+
"""
|
|
102
|
+
if gate_key not in st.session_state:
|
|
103
|
+
st.session_state[gate_key] = False
|
|
104
|
+
|
|
105
|
+
if st.session_state[gate_key]:
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
status = render_environment_status(env, connection=options.connection)
|
|
109
|
+
st.divider()
|
|
110
|
+
_, center, _ = st.columns([1, 2, 1])
|
|
111
|
+
with center:
|
|
112
|
+
st.markdown(f"### {title}")
|
|
113
|
+
st.write(
|
|
114
|
+
body
|
|
115
|
+
or (
|
|
116
|
+
"Click below to create the Streamlit CoCo session, connect the Cortex Code "
|
|
117
|
+
"CLI, and open the chat panel. Nothing starts until you confirm."
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
if not status.ready:
|
|
121
|
+
st.warning(
|
|
122
|
+
"Fix the CoCo SDK / CLI issues above before starting — "
|
|
123
|
+
"the chat may fail to connect."
|
|
124
|
+
)
|
|
125
|
+
if st.button(button_label, type="primary", use_container_width=True):
|
|
126
|
+
st.session_state[gate_key] = True
|
|
127
|
+
session = CocoSession(options=options, key=session_key)
|
|
128
|
+
if warm_up:
|
|
129
|
+
session.start()
|
|
130
|
+
st.session_state[session_key] = session
|
|
131
|
+
st.rerun()
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def reset_session(
|
|
136
|
+
options: CocoOptions,
|
|
137
|
+
*,
|
|
138
|
+
session_key: str = "coco",
|
|
139
|
+
warm_up: bool = False,
|
|
140
|
+
) -> CocoSession:
|
|
141
|
+
"""Replace the stored session with a fresh one."""
|
|
142
|
+
existing = st.session_state.get(session_key)
|
|
143
|
+
if isinstance(existing, CocoSession):
|
|
144
|
+
existing.reset()
|
|
145
|
+
existing.close()
|
|
146
|
+
session = CocoSession(options=options, key=session_key)
|
|
147
|
+
if warm_up:
|
|
148
|
+
session.start()
|
|
149
|
+
st.session_state[session_key] = session
|
|
150
|
+
return session
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def stop_session(
|
|
154
|
+
*,
|
|
155
|
+
session_key: str = "coco",
|
|
156
|
+
gate_key: str = "coco_started",
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Tear down the session and return to the start gate."""
|
|
159
|
+
existing = st.session_state.get(session_key)
|
|
160
|
+
if isinstance(existing, CocoSession):
|
|
161
|
+
existing.reset()
|
|
162
|
+
existing.close()
|
|
163
|
+
st.session_state.pop(session_key, None)
|
|
164
|
+
st.session_state[gate_key] = False
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def chat_input_bar(
|
|
168
|
+
session: CocoSession,
|
|
169
|
+
*,
|
|
170
|
+
placeholder: str = "Ask CoCo…",
|
|
171
|
+
connecting_placeholder: str = "Starting CoCo…",
|
|
172
|
+
key: str | None = None,
|
|
173
|
+
) -> str | None:
|
|
174
|
+
"""``st.chat_input`` wired for connect/run state; sends on submit.
|
|
175
|
+
|
|
176
|
+
Stays enabled while CoCo is connecting (prompts are queued). Only a failed
|
|
177
|
+
boot disables input. ``panel()`` triggers a full rerun when connect finishes
|
|
178
|
+
so placeholder/status stay in sync.
|
|
179
|
+
"""
|
|
180
|
+
connecting = session.status == CocoRunStatus.CONNECTING
|
|
181
|
+
failed_boot = session.status == CocoRunStatus.ERROR and not session.is_ready
|
|
182
|
+
kwargs: dict[str, Any] = {}
|
|
183
|
+
if key is not None:
|
|
184
|
+
kwargs["key"] = key
|
|
185
|
+
if "submit_mode" in inspect.signature(st.chat_input).parameters:
|
|
186
|
+
kwargs["disabled"] = failed_boot
|
|
187
|
+
kwargs["submit_mode"] = "disable" if session.is_running else "submit"
|
|
188
|
+
else:
|
|
189
|
+
# Streamlit < 1.59: no submit_mode — disable input while a turn is running.
|
|
190
|
+
kwargs["disabled"] = failed_boot or session.is_running
|
|
191
|
+
|
|
192
|
+
prompt = st.chat_input(
|
|
193
|
+
connecting_placeholder if connecting else placeholder,
|
|
194
|
+
**kwargs,
|
|
195
|
+
)
|
|
196
|
+
if prompt:
|
|
197
|
+
send_prompt(session, prompt)
|
|
198
|
+
return prompt
|
streamlit_coco/bridge.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Bridge helpers between CocoSession and Streamlit component state."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from streamlit_coco.debug import is_debug_mode
|
|
8
|
+
from streamlit_coco.session import CocoSession
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_component_data(
|
|
12
|
+
session: CocoSession,
|
|
13
|
+
*,
|
|
14
|
+
placeholder: str,
|
|
15
|
+
show_tool_details: bool,
|
|
16
|
+
show_thinking: bool,
|
|
17
|
+
show_structured_inline: bool,
|
|
18
|
+
height: int | str,
|
|
19
|
+
include_transcript: bool = True,
|
|
20
|
+
) -> dict[str, Any]:
|
|
21
|
+
pending = session.permission_manager.active_pending()
|
|
22
|
+
transcript = session.get_transcript_snapshot() if include_transcript else []
|
|
23
|
+
return {
|
|
24
|
+
"transcript": transcript,
|
|
25
|
+
"status": session.status.value,
|
|
26
|
+
"pending_approval": pending.to_dict() if pending else None,
|
|
27
|
+
"last_error": session.last_error,
|
|
28
|
+
"header": {
|
|
29
|
+
"title": "CoCo",
|
|
30
|
+
"model": session.options.model,
|
|
31
|
+
"connection": session.options.connection,
|
|
32
|
+
"permission_mode": session.options.permission_mode,
|
|
33
|
+
},
|
|
34
|
+
"placeholder": placeholder,
|
|
35
|
+
"show_tool_details": show_tool_details,
|
|
36
|
+
"debug_mode": is_debug_mode(),
|
|
37
|
+
"show_thinking": show_thinking,
|
|
38
|
+
"show_structured_inline": show_structured_inline,
|
|
39
|
+
"height": height,
|
|
40
|
+
"scroll_token": session.get_revision(),
|
|
41
|
+
"needs_polling": session.needs_polling,
|
|
42
|
+
}
|