cjm-transcript-segment-align 0.0.1__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.
- cjm_transcript_segment_align/__init__.py +1 -0
- cjm_transcript_segment_align/_modidx.py +101 -0
- cjm_transcript_segment_align/components/__init__.py +0 -0
- cjm_transcript_segment_align/components/handlers.py +331 -0
- cjm_transcript_segment_align/components/helpers.py +85 -0
- cjm_transcript_segment_align/components/keyboard_config.py +323 -0
- cjm_transcript_segment_align/components/step_renderer.py +624 -0
- cjm_transcript_segment_align/html_ids.py +44 -0
- cjm_transcript_segment_align/routes/__init__.py +0 -0
- cjm_transcript_segment_align/routes/chrome.py +169 -0
- cjm_transcript_segment_align/routes/forced_alignment.py +396 -0
- cjm_transcript_segment_align/services/__init__.py +0 -0
- cjm_transcript_segment_align/services/forced_alignment.py +301 -0
- cjm_transcript_segment_align-0.0.1.dist-info/LICENSE +201 -0
- cjm_transcript_segment_align-0.0.1.dist-info/METADATA +777 -0
- cjm_transcript_segment_align-0.0.1.dist-info/RECORD +19 -0
- cjm_transcript_segment_align-0.0.1.dist-info/WHEEL +5 -0
- cjm_transcript_segment_align-0.0.1.dist-info/entry_points.txt +2 -0
- cjm_transcript_segment_align-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.1"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Autogenerated by nbdev
|
|
2
|
+
|
|
3
|
+
d = { 'settings': { 'branch': 'main',
|
|
4
|
+
'doc_baseurl': '/cjm-transcript-segment-align',
|
|
5
|
+
'doc_host': 'https://cj-mills.github.io',
|
|
6
|
+
'git_url': 'https://github.com/cj-mills/cjm-transcript-segment-align',
|
|
7
|
+
'lib_path': 'cjm_transcript_segment_align'},
|
|
8
|
+
'syms': { 'cjm_transcript_segment_align.components.handlers': { 'cjm_transcript_segment_align.components.handlers._find_session_id': ( 'components/handlers.html#_find_session_id',
|
|
9
|
+
'cjm_transcript_segment_align/components/handlers.py'),
|
|
10
|
+
'cjm_transcript_segment_align.components.handlers.create_align_init_chrome_wrapper': ( 'components/handlers.html#create_align_init_chrome_wrapper',
|
|
11
|
+
'cjm_transcript_segment_align/components/handlers.py'),
|
|
12
|
+
'cjm_transcript_segment_align.components.handlers.create_seg_init_chrome_wrapper': ( 'components/handlers.html#create_seg_init_chrome_wrapper',
|
|
13
|
+
'cjm_transcript_segment_align/components/handlers.py'),
|
|
14
|
+
'cjm_transcript_segment_align.components.handlers.wrap_align_mutation_handler': ( 'components/handlers.html#wrap_align_mutation_handler',
|
|
15
|
+
'cjm_transcript_segment_align/components/handlers.py'),
|
|
16
|
+
'cjm_transcript_segment_align.components.handlers.wrap_seg_mutation_handler': ( 'components/handlers.html#wrap_seg_mutation_handler',
|
|
17
|
+
'cjm_transcript_segment_align/components/handlers.py')},
|
|
18
|
+
'cjm_transcript_segment_align.components.helpers': { 'cjm_transcript_segment_align.components.helpers.check_alignment_ready': ( 'components/helpers.html#check_alignment_ready',
|
|
19
|
+
'cjm_transcript_segment_align/components/helpers.py'),
|
|
20
|
+
'cjm_transcript_segment_align.components.helpers.extract_alignment_state': ( 'components/helpers.html#extract_alignment_state',
|
|
21
|
+
'cjm_transcript_segment_align/components/helpers.py'),
|
|
22
|
+
'cjm_transcript_segment_align.components.helpers.extract_seg_state': ( 'components/helpers.html#extract_seg_state',
|
|
23
|
+
'cjm_transcript_segment_align/components/helpers.py'),
|
|
24
|
+
'cjm_transcript_segment_align.components.helpers.get_chunk_count': ( 'components/helpers.html#get_chunk_count',
|
|
25
|
+
'cjm_transcript_segment_align/components/helpers.py'),
|
|
26
|
+
'cjm_transcript_segment_align.components.helpers.get_segment_count': ( 'components/helpers.html#get_segment_count',
|
|
27
|
+
'cjm_transcript_segment_align/components/helpers.py')},
|
|
28
|
+
'cjm_transcript_segment_align.components.keyboard_config': { 'cjm_transcript_segment_align.components.keyboard_config.build_combined_kb_system': ( 'components/keyboard_config.html#build_combined_kb_system',
|
|
29
|
+
'cjm_transcript_segment_align/components/keyboard_config.py'),
|
|
30
|
+
'cjm_transcript_segment_align.components.keyboard_config.generate_zone_change_js': ( 'components/keyboard_config.html#generate_zone_change_js',
|
|
31
|
+
'cjm_transcript_segment_align/components/keyboard_config.py'),
|
|
32
|
+
'cjm_transcript_segment_align.components.keyboard_config.render_keyboard_hints_collapsible': ( 'components/keyboard_config.html#render_keyboard_hints_collapsible',
|
|
33
|
+
'cjm_transcript_segment_align/components/keyboard_config.py')},
|
|
34
|
+
'cjm_transcript_segment_align.components.step_renderer': { 'cjm_transcript_segment_align.components.step_renderer._placeholder': ( 'components/step_renderer.html#_placeholder',
|
|
35
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
36
|
+
'cjm_transcript_segment_align.components.step_renderer._render_alignment_column': ( 'components/step_renderer.html#_render_alignment_column',
|
|
37
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
38
|
+
'cjm_transcript_segment_align.components.step_renderer._render_column_header': ( 'components/step_renderer.html#_render_column_header',
|
|
39
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
40
|
+
'cjm_transcript_segment_align.components.step_renderer._render_keyboard_system_container': ( 'components/step_renderer.html#_render_keyboard_system_container',
|
|
41
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
42
|
+
'cjm_transcript_segment_align.components.step_renderer._render_seg_column': ( 'components/step_renderer.html#_render_seg_column',
|
|
43
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
44
|
+
'cjm_transcript_segment_align.components.step_renderer._render_shared_chrome': ( 'components/step_renderer.html#_render_shared_chrome',
|
|
45
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
46
|
+
'cjm_transcript_segment_align.components.step_renderer.render_align_mini_stats_badge': ( 'components/step_renderer.html#render_align_mini_stats_badge',
|
|
47
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
48
|
+
'cjm_transcript_segment_align.components.step_renderer.render_alignment_status': ( 'components/step_renderer.html#render_alignment_status',
|
|
49
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
50
|
+
'cjm_transcript_segment_align.components.step_renderer.render_alignment_status_text': ( 'components/step_renderer.html#render_alignment_status_text',
|
|
51
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
52
|
+
'cjm_transcript_segment_align.components.step_renderer.render_combined_step': ( 'components/step_renderer.html#render_combined_step',
|
|
53
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
54
|
+
'cjm_transcript_segment_align.components.step_renderer.render_footer_inner_content': ( 'components/step_renderer.html#render_footer_inner_content',
|
|
55
|
+
'cjm_transcript_segment_align/components/step_renderer.py'),
|
|
56
|
+
'cjm_transcript_segment_align.components.step_renderer.render_seg_mini_stats_badge': ( 'components/step_renderer.html#render_seg_mini_stats_badge',
|
|
57
|
+
'cjm_transcript_segment_align/components/step_renderer.py')},
|
|
58
|
+
'cjm_transcript_segment_align.html_ids': { 'cjm_transcript_segment_align.html_ids.CombinedHtmlIds': ( 'html_ids.html#combinedhtmlids',
|
|
59
|
+
'cjm_transcript_segment_align/html_ids.py'),
|
|
60
|
+
'cjm_transcript_segment_align.html_ids.CombinedHtmlIds.as_selector': ( 'html_ids.html#combinedhtmlids.as_selector',
|
|
61
|
+
'cjm_transcript_segment_align/html_ids.py')},
|
|
62
|
+
'cjm_transcript_segment_align.routes.chrome': { 'cjm_transcript_segment_align.routes.chrome._handle_switch_chrome': ( 'routes/chrome.html#_handle_switch_chrome',
|
|
63
|
+
'cjm_transcript_segment_align/routes/chrome.py'),
|
|
64
|
+
'cjm_transcript_segment_align.routes.chrome.init_chrome_router': ( 'routes/chrome.html#init_chrome_router',
|
|
65
|
+
'cjm_transcript_segment_align/routes/chrome.py')},
|
|
66
|
+
'cjm_transcript_segment_align.routes.forced_alignment': { 'cjm_transcript_segment_align.routes.forced_alignment._handle_fa_toggle': ( 'routes/forced_alignment.html#_handle_fa_toggle',
|
|
67
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py'),
|
|
68
|
+
'cjm_transcript_segment_align.routes.forced_alignment._handle_fa_trigger': ( 'routes/forced_alignment.html#_handle_fa_trigger',
|
|
69
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py'),
|
|
70
|
+
'cjm_transcript_segment_align.routes.forced_alignment.init_forced_alignment_routers': ( 'routes/forced_alignment.html#init_forced_alignment_routers',
|
|
71
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py'),
|
|
72
|
+
'cjm_transcript_segment_align.routes.forced_alignment.render_fa_controls': ( 'routes/forced_alignment.html#render_fa_controls',
|
|
73
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py'),
|
|
74
|
+
'cjm_transcript_segment_align.routes.forced_alignment.render_fa_progress': ( 'routes/forced_alignment.html#render_fa_progress',
|
|
75
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py'),
|
|
76
|
+
'cjm_transcript_segment_align.routes.forced_alignment.render_fa_toggle': ( 'routes/forced_alignment.html#render_fa_toggle',
|
|
77
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py'),
|
|
78
|
+
'cjm_transcript_segment_align.routes.forced_alignment.render_fa_trigger_button': ( 'routes/forced_alignment.html#render_fa_trigger_button',
|
|
79
|
+
'cjm_transcript_segment_align/routes/forced_alignment.py')},
|
|
80
|
+
'cjm_transcript_segment_align.services.forced_alignment': { 'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService': ( 'services/forced_alignment.html#forcedalignmentservice',
|
|
81
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
82
|
+
'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService.__init__': ( 'services/forced_alignment.html#forcedalignmentservice.__init__',
|
|
83
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
84
|
+
'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService.align_and_split': ( 'services/forced_alignment.html#forcedalignmentservice.align_and_split',
|
|
85
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
86
|
+
'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService.align_and_split_async': ( 'services/forced_alignment.html#forcedalignmentservice.align_and_split_async',
|
|
87
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
88
|
+
'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService.align_and_split_combined_async': ( 'services/forced_alignment.html#forcedalignmentservice.align_and_split_combined_async',
|
|
89
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
90
|
+
'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService.ensure_loaded': ( 'services/forced_alignment.html#forcedalignmentservice.ensure_loaded',
|
|
91
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
92
|
+
'cjm_transcript_segment_align.services.forced_alignment.ForcedAlignmentService.is_available': ( 'services/forced_alignment.html#forcedalignmentservice.is_available',
|
|
93
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
94
|
+
'cjm_transcript_segment_align.services.forced_alignment._strip_punct': ( 'services/forced_alignment.html#_strip_punct',
|
|
95
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
96
|
+
'cjm_transcript_segment_align.services.forced_alignment.assign_words_to_chunks': ( 'services/forced_alignment.html#assign_words_to_chunks',
|
|
97
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
98
|
+
'cjm_transcript_segment_align.services.forced_alignment.build_segments_from_alignment': ( 'services/forced_alignment.html#build_segments_from_alignment',
|
|
99
|
+
'cjm_transcript_segment_align/services/forced_alignment.py'),
|
|
100
|
+
'cjm_transcript_segment_align.services.forced_alignment.map_fa_words_to_text': ( 'services/forced_alignment.html#map_fa_words_to_text',
|
|
101
|
+
'cjm_transcript_segment_align/services/forced_alignment.py')}}}
|
|
File without changes
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""Handler wrappers for cross-domain coordination (alignment status updates)"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/handlers.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['wrapped_seg_split', 'wrapped_seg_merge', 'wrapped_seg_undo', 'wrapped_seg_reset', 'wrapped_seg_ai_split',
|
|
7
|
+
'wrap_seg_mutation_handler', 'wrap_align_mutation_handler', 'create_seg_init_chrome_wrapper',
|
|
8
|
+
'create_align_init_chrome_wrapper']
|
|
9
|
+
|
|
10
|
+
# %% ../../nbs/components/handlers.ipynb #c3d4e5f6
|
|
11
|
+
import asyncio
|
|
12
|
+
from functools import wraps
|
|
13
|
+
from typing import Callable, Any, List
|
|
14
|
+
|
|
15
|
+
from fasthtml.common import Div, Span, Button
|
|
16
|
+
|
|
17
|
+
from cjm_fasthtml_interactions.core.state_store import get_session_id
|
|
18
|
+
from cjm_fasthtml_card_stack.components.controls import render_width_slider
|
|
19
|
+
from cjm_fasthtml_daisyui.components.data_display.badge import badge, badge_styles, badge_sizes
|
|
20
|
+
from cjm_fasthtml_tailwind.utilities.layout import display_tw
|
|
21
|
+
from cjm_fasthtml_tailwind.core.base import combine_classes
|
|
22
|
+
|
|
23
|
+
from ..html_ids import CombinedHtmlIds
|
|
24
|
+
from cjm_transcript_segment_align.components.step_renderer import (
|
|
25
|
+
render_alignment_status, render_seg_mini_stats_badge, render_align_mini_stats_badge,
|
|
26
|
+
render_footer_inner_content,
|
|
27
|
+
)
|
|
28
|
+
from cjm_transcript_segment_align.components.keyboard_config import (
|
|
29
|
+
build_combined_kb_system, render_keyboard_hints_collapsible,
|
|
30
|
+
generate_zone_change_js, SWITCH_CHROME_BTN_ID,
|
|
31
|
+
)
|
|
32
|
+
from cjm_transcript_segmentation.models import TextSegment, SegmentationUrls
|
|
33
|
+
from cjm_transcript_segmentation.routes.core import WorkflowStateStore
|
|
34
|
+
from cjm_transcript_segmentation.routes.handlers import (
|
|
35
|
+
SegInitResult, _handle_seg_init, _handle_seg_split, _handle_seg_merge,
|
|
36
|
+
_handle_seg_undo, _handle_seg_reset, _handle_seg_ai_split,
|
|
37
|
+
)
|
|
38
|
+
from cjm_transcript_segmentation.components.step_renderer import (
|
|
39
|
+
render_toolbar, render_seg_footer_content,
|
|
40
|
+
)
|
|
41
|
+
from cjm_transcript_segmentation.components.card_stack_config import (
|
|
42
|
+
SEG_CS_CONFIG, SEG_CS_IDS,
|
|
43
|
+
)
|
|
44
|
+
from cjm_transcript_vad_align.models import AlignmentUrls, VADChunk
|
|
45
|
+
from cjm_transcript_vad_align.routes.core import (
|
|
46
|
+
WorkflowStateStore as AlignWorkflowStateStore,
|
|
47
|
+
)
|
|
48
|
+
from cjm_transcript_vad_align.routes.handlers import (
|
|
49
|
+
AlignInitResult, _handle_align_init,
|
|
50
|
+
)
|
|
51
|
+
from cjm_transcript_vad_align.services.alignment import AlignmentService
|
|
52
|
+
from cjm_transcript_source_select.services.source import SourceService
|
|
53
|
+
|
|
54
|
+
# %% ../../nbs/components/handlers.ipynb #e5f6a7b8
|
|
55
|
+
def _find_session_id(args, kwargs):
|
|
56
|
+
"""Find session_id from args or kwargs."""
|
|
57
|
+
# First check kwargs
|
|
58
|
+
if 'sess' in kwargs:
|
|
59
|
+
try:
|
|
60
|
+
return get_session_id(kwargs['sess'])
|
|
61
|
+
except:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
# Search args - session objects have a 'session' key
|
|
65
|
+
for arg in args:
|
|
66
|
+
try:
|
|
67
|
+
session_id = get_session_id(arg)
|
|
68
|
+
if session_id:
|
|
69
|
+
return session_id
|
|
70
|
+
except:
|
|
71
|
+
continue
|
|
72
|
+
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def wrap_seg_mutation_handler(
|
|
77
|
+
handler: Callable, # Handler function to wrap
|
|
78
|
+
) -> Callable: # Wrapped handler that appends alignment status OOB
|
|
79
|
+
"""Wrap a segmentation mutation handler to add alignment status OOB.
|
|
80
|
+
|
|
81
|
+
The handler is expected to take (state_store, workflow_id, ...) as first params.
|
|
82
|
+
"""
|
|
83
|
+
@wraps(handler)
|
|
84
|
+
async def wrapped(
|
|
85
|
+
state_store: WorkflowStateStore,
|
|
86
|
+
workflow_id: str,
|
|
87
|
+
*args,
|
|
88
|
+
**kwargs
|
|
89
|
+
):
|
|
90
|
+
# Call the original handler (async or sync)
|
|
91
|
+
if asyncio.iscoroutinefunction(handler):
|
|
92
|
+
result = await handler(state_store, workflow_id, *args, **kwargs)
|
|
93
|
+
else:
|
|
94
|
+
result = handler(state_store, workflow_id, *args, **kwargs)
|
|
95
|
+
|
|
96
|
+
# Find session_id from args/kwargs
|
|
97
|
+
session_id = _find_session_id(args, kwargs)
|
|
98
|
+
|
|
99
|
+
if session_id is not None:
|
|
100
|
+
# Get counts from state for alignment status
|
|
101
|
+
workflow_state = state_store.get_state(workflow_id, session_id)
|
|
102
|
+
step_states = workflow_state.get("step_states", {})
|
|
103
|
+
segment_count = len(step_states.get("segmentation", {}).get("segments", []))
|
|
104
|
+
chunk_count = len(step_states.get("alignment", {}).get("vad_chunks", []))
|
|
105
|
+
|
|
106
|
+
# Append alignment status OOB to result
|
|
107
|
+
return (*result, render_alignment_status(segment_count, chunk_count, oob=True))
|
|
108
|
+
|
|
109
|
+
# If we couldn't find session_id, just return the result without the status update
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
return wrapped
|
|
113
|
+
|
|
114
|
+
# %% ../../nbs/components/handlers.ipynb #f6a7b8c9
|
|
115
|
+
def wrap_align_mutation_handler(
|
|
116
|
+
handler: Callable, # Handler function to wrap
|
|
117
|
+
) -> Callable: # Wrapped handler that appends alignment status OOB
|
|
118
|
+
"""Wrap an alignment mutation handler to add alignment status OOB.
|
|
119
|
+
|
|
120
|
+
The handler is expected to take (state_store, workflow_id, ...) as first params.
|
|
121
|
+
"""
|
|
122
|
+
@wraps(handler)
|
|
123
|
+
async def wrapped(
|
|
124
|
+
state_store: WorkflowStateStore,
|
|
125
|
+
workflow_id: str,
|
|
126
|
+
*args,
|
|
127
|
+
**kwargs
|
|
128
|
+
):
|
|
129
|
+
# Call the original handler (async or sync)
|
|
130
|
+
if asyncio.iscoroutinefunction(handler):
|
|
131
|
+
result = await handler(state_store, workflow_id, *args, **kwargs)
|
|
132
|
+
else:
|
|
133
|
+
result = handler(state_store, workflow_id, *args, **kwargs)
|
|
134
|
+
|
|
135
|
+
# Find session_id from args/kwargs
|
|
136
|
+
session_id = _find_session_id(args, kwargs)
|
|
137
|
+
|
|
138
|
+
if session_id is not None:
|
|
139
|
+
# Get counts from state for alignment status
|
|
140
|
+
workflow_state = state_store.get_state(workflow_id, session_id)
|
|
141
|
+
step_states = workflow_state.get("step_states", {})
|
|
142
|
+
segment_count = len(step_states.get("segmentation", {}).get("segments", []))
|
|
143
|
+
chunk_count = len(step_states.get("alignment", {}).get("vad_chunks", []))
|
|
144
|
+
|
|
145
|
+
# Append alignment status OOB to result
|
|
146
|
+
return (*result, render_alignment_status(segment_count, chunk_count, oob=True))
|
|
147
|
+
|
|
148
|
+
# If we couldn't find session_id, just return the result without the status update
|
|
149
|
+
return result
|
|
150
|
+
|
|
151
|
+
return wrapped
|
|
152
|
+
|
|
153
|
+
# %% ../../nbs/components/handlers.ipynb #rudnoqfwqe
|
|
154
|
+
def create_seg_init_chrome_wrapper(
|
|
155
|
+
align_urls:AlignmentUrls, # URL bundle for alignment routes (for KB system)
|
|
156
|
+
switch_chrome_url:str, # URL for chrome switching (for KB system)
|
|
157
|
+
fa_trigger_url:str="", # URL for forced alignment trigger (optional)
|
|
158
|
+
fa_toggle_url:str="", # URL for forced alignment toggle (optional)
|
|
159
|
+
fa_available:bool=False, # Whether forced alignment plugin is available
|
|
160
|
+
) -> Callable: # Wrapped handler that builds KB system and shared chrome
|
|
161
|
+
"""Create a wrapper for seg init that builds combined KB system and shared chrome.
|
|
162
|
+
|
|
163
|
+
This is a factory that captures the URLs needed for KB system assembly.
|
|
164
|
+
Optionally includes forced alignment controls if FA plugin is available.
|
|
165
|
+
"""
|
|
166
|
+
# Import here to avoid circular imports (routes module imports from components)
|
|
167
|
+
from cjm_transcript_segment_align.routes.forced_alignment import render_fa_controls
|
|
168
|
+
|
|
169
|
+
async def wrapped_seg_init(
|
|
170
|
+
state_store:WorkflowStateStore,
|
|
171
|
+
workflow_id:str,
|
|
172
|
+
source_service:SourceService,
|
|
173
|
+
segmentation_service:Any,
|
|
174
|
+
request:Any,
|
|
175
|
+
sess:Any,
|
|
176
|
+
urls:SegmentationUrls,
|
|
177
|
+
visible_count:int=5,
|
|
178
|
+
card_width:int=40,
|
|
179
|
+
):
|
|
180
|
+
"""Wrapped seg init that adds KB system and shared chrome."""
|
|
181
|
+
# Call pure domain handler
|
|
182
|
+
result: SegInitResult = await _handle_seg_init(
|
|
183
|
+
state_store, workflow_id, source_service, segmentation_service,
|
|
184
|
+
request, sess, urls, visible_count, card_width,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
session_id = get_session_id(sess)
|
|
188
|
+
|
|
189
|
+
# Get VAD chunk count for alignment status
|
|
190
|
+
workflow_state = state_store.get_state(workflow_id, session_id)
|
|
191
|
+
seg_state = workflow_state.get("step_states", {}).get("segmentation", {})
|
|
192
|
+
chunk_count = len(workflow_state.get("step_states", {}).get("alignment", {}).get("vad_chunks", []))
|
|
193
|
+
segment_count = len(result.segments)
|
|
194
|
+
|
|
195
|
+
# Build combined KB system with both zones
|
|
196
|
+
kb_manager, kb_system = build_combined_kb_system(urls, align_urls)
|
|
197
|
+
|
|
198
|
+
# OOB swap for stable keyboard system container
|
|
199
|
+
kb_system_oob = Div(
|
|
200
|
+
kb_system.script,
|
|
201
|
+
kb_system.hidden_inputs,
|
|
202
|
+
kb_system.action_buttons,
|
|
203
|
+
id=CombinedHtmlIds.KEYBOARD_SYSTEM,
|
|
204
|
+
hx_swap_oob="innerHTML"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Zone change JS (goes in response for browser to execute)
|
|
208
|
+
zone_change_js = generate_zone_change_js(switch_chrome_url)
|
|
209
|
+
|
|
210
|
+
# Hidden chrome switch button for HTMX trigger
|
|
211
|
+
chrome_switch_btn = Button(
|
|
212
|
+
id=SWITCH_CHROME_BTN_ID,
|
|
213
|
+
cls=str(display_tw.hidden),
|
|
214
|
+
hx_post=switch_chrome_url,
|
|
215
|
+
hx_include=f"#{CombinedHtmlIds.ACTIVE_COLUMN_INPUT}",
|
|
216
|
+
hx_swap="none",
|
|
217
|
+
hx_swap_oob="true",
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Update hints to include zone switch info
|
|
221
|
+
hints_oob = Div(
|
|
222
|
+
render_keyboard_hints_collapsible(kb_manager, include_zone_switch=True),
|
|
223
|
+
id=CombinedHtmlIds.SHARED_HINTS,
|
|
224
|
+
hx_swap_oob="innerHTML"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Toolbar OOB
|
|
228
|
+
toolbar_oob = Div(
|
|
229
|
+
render_toolbar(
|
|
230
|
+
reset_url=urls.reset, ai_split_url=urls.ai_split, undo_url=urls.undo,
|
|
231
|
+
can_undo=(result.history_depth > 0),
|
|
232
|
+
visible_count=result.visible_count,
|
|
233
|
+
is_auto_mode=result.is_auto_mode,
|
|
234
|
+
),
|
|
235
|
+
id=CombinedHtmlIds.SHARED_TOOLBAR,
|
|
236
|
+
hx_swap_oob="innerHTML"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Controls OOB (width slider)
|
|
240
|
+
controls_oob = Div(
|
|
241
|
+
render_width_slider(SEG_CS_CONFIG, SEG_CS_IDS, card_width=result.card_width),
|
|
242
|
+
id=CombinedHtmlIds.SHARED_CONTROLS,
|
|
243
|
+
hx_swap_oob="innerHTML"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Footer OOB with alignment status
|
|
247
|
+
footer_oob = Div(
|
|
248
|
+
render_footer_inner_content(
|
|
249
|
+
render_seg_footer_content(result.segments, result.focused_index),
|
|
250
|
+
segment_count, chunk_count
|
|
251
|
+
),
|
|
252
|
+
id=CombinedHtmlIds.SHARED_FOOTER,
|
|
253
|
+
hx_swap_oob="innerHTML"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
# Mini-stats badge OOB
|
|
257
|
+
mini_stats_oob = render_seg_mini_stats_badge(result.segments, oob=True)
|
|
258
|
+
|
|
259
|
+
# FA controls OOB (trigger button or toggle based on state)
|
|
260
|
+
active_presplit = seg_state.get("active_presplit")
|
|
261
|
+
fa_controls_oob = render_fa_controls(
|
|
262
|
+
trigger_url=fa_trigger_url,
|
|
263
|
+
toggle_url=fa_toggle_url,
|
|
264
|
+
active_presplit=active_presplit,
|
|
265
|
+
fa_available=fa_available,
|
|
266
|
+
oob=True,
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
result.column_body, kb_system_oob, zone_change_js, chrome_switch_btn,
|
|
271
|
+
hints_oob, toolbar_oob, controls_oob, footer_oob, mini_stats_oob,
|
|
272
|
+
fa_controls_oob,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return wrapped_seg_init
|
|
276
|
+
|
|
277
|
+
# %% ../../nbs/components/handlers.ipynb #ecvyiypdxk
|
|
278
|
+
def create_align_init_chrome_wrapper() -> Callable: # Wrapped handler that adds alignment status
|
|
279
|
+
"""Create a wrapper for align init that adds mini-stats and alignment status.
|
|
280
|
+
|
|
281
|
+
Alignment init is simpler than seg init - it doesn't need to build the
|
|
282
|
+
full KB system (seg init handles that). It just updates alignment-specific
|
|
283
|
+
chrome and the alignment status badge.
|
|
284
|
+
"""
|
|
285
|
+
async def wrapped_align_init(
|
|
286
|
+
state_store:WorkflowStateStore,
|
|
287
|
+
workflow_id:str,
|
|
288
|
+
source_service:SourceService,
|
|
289
|
+
alignment_service:AlignmentService,
|
|
290
|
+
request:Any,
|
|
291
|
+
sess:Any,
|
|
292
|
+
urls:AlignmentUrls,
|
|
293
|
+
visible_count:int=5,
|
|
294
|
+
card_width:int=40,
|
|
295
|
+
):
|
|
296
|
+
"""Wrapped align init that adds mini-stats and alignment status."""
|
|
297
|
+
# Call pure domain handler
|
|
298
|
+
result: AlignInitResult = await _handle_align_init(
|
|
299
|
+
state_store, workflow_id, source_service, alignment_service,
|
|
300
|
+
request, sess, urls, visible_count, card_width,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
session_id = get_session_id(sess)
|
|
304
|
+
|
|
305
|
+
# Get segment count for alignment status
|
|
306
|
+
workflow_state = state_store.get_state(workflow_id, session_id)
|
|
307
|
+
segment_count = len(workflow_state.get("step_states", {}).get("segmentation", {}).get("segments", []))
|
|
308
|
+
chunk_count = len(result.chunks)
|
|
309
|
+
|
|
310
|
+
# Mini-stats badge OOB
|
|
311
|
+
mini_stats_oob = render_align_mini_stats_badge(result.chunks, oob=True)
|
|
312
|
+
|
|
313
|
+
# Alignment status OOB
|
|
314
|
+
alignment_status_oob = render_alignment_status(segment_count, chunk_count, oob=True)
|
|
315
|
+
|
|
316
|
+
return (result.column_body, mini_stats_oob, alignment_status_oob)
|
|
317
|
+
|
|
318
|
+
return wrapped_align_init
|
|
319
|
+
|
|
320
|
+
# %% ../../nbs/components/handlers.ipynb #b8c9d0e1
|
|
321
|
+
# Segmentation mutation handlers (change segment count, but NOT init)
|
|
322
|
+
# These add alignment status OOB but don't build KB system or shared chrome.
|
|
323
|
+
# Init handlers use the factory wrappers (create_seg_init_chrome_wrapper, etc.)
|
|
324
|
+
wrapped_seg_split = wrap_seg_mutation_handler(_handle_seg_split)
|
|
325
|
+
wrapped_seg_merge = wrap_seg_mutation_handler(_handle_seg_merge)
|
|
326
|
+
wrapped_seg_undo = wrap_seg_mutation_handler(_handle_seg_undo)
|
|
327
|
+
wrapped_seg_reset = wrap_seg_mutation_handler(_handle_seg_reset)
|
|
328
|
+
wrapped_seg_ai_split = wrap_seg_mutation_handler(_handle_seg_ai_split)
|
|
329
|
+
|
|
330
|
+
# Note: Alignment has no mutation handlers other than init.
|
|
331
|
+
# Alignment init uses create_align_init_chrome_wrapper() factory.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""State extraction helpers for cross-domain coordination in Phase 2 combined step"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/components/helpers.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['SEG_DEFAULT_VISIBLE_COUNT', 'SEG_DEFAULT_CARD_WIDTH', 'ALIGN_DEFAULT_VISIBLE_COUNT', 'ALIGN_DEFAULT_CARD_WIDTH',
|
|
7
|
+
'check_alignment_ready', 'extract_seg_state', 'extract_alignment_state', 'get_segment_count',
|
|
8
|
+
'get_chunk_count']
|
|
9
|
+
|
|
10
|
+
# %% ../../nbs/components/helpers.ipynb #c3d4e5f6
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
from cjm_fasthtml_interactions.core.context import InteractionContext
|
|
14
|
+
from cjm_fasthtml_card_stack.core.constants import DEFAULT_VISIBLE_COUNT, DEFAULT_CARD_WIDTH
|
|
15
|
+
|
|
16
|
+
from cjm_transcript_segmentation.models import TextSegment
|
|
17
|
+
from cjm_transcript_vad_align.models import VADChunk
|
|
18
|
+
|
|
19
|
+
# %% ../../nbs/components/helpers.ipynb #e5f6a7b8
|
|
20
|
+
def check_alignment_ready(
|
|
21
|
+
segment_count:int, # Number of text segments
|
|
22
|
+
chunk_count:int, # Number of VAD chunks
|
|
23
|
+
) -> bool: # True if counts match for 1:1 alignment
|
|
24
|
+
"""Check if segment and VAD chunk counts match for 1:1 alignment."""
|
|
25
|
+
return segment_count > 0 and chunk_count > 0 and segment_count == chunk_count
|
|
26
|
+
|
|
27
|
+
# %% ../../nbs/components/helpers.ipynb #a7b8c9d0
|
|
28
|
+
# Default values for segmentation card stack (wider cards for text)
|
|
29
|
+
SEG_DEFAULT_VISIBLE_COUNT = 3
|
|
30
|
+
SEG_DEFAULT_CARD_WIDTH = 80
|
|
31
|
+
|
|
32
|
+
# %% ../../nbs/components/helpers.ipynb #b8c9d0e1
|
|
33
|
+
def extract_seg_state(
|
|
34
|
+
ctx:InteractionContext, # Interaction context with state
|
|
35
|
+
) -> Dict[str, Any]: # Extracted state values
|
|
36
|
+
"""Extract segmentation state as explicit values for renderers."""
|
|
37
|
+
state = ctx.state.get("step_states", {}).get("segmentation", {})
|
|
38
|
+
return {
|
|
39
|
+
"is_initialized": state.get("is_initialized", False),
|
|
40
|
+
"segments": [TextSegment.from_dict(s) for s in state.get("segments", [])],
|
|
41
|
+
"focused_index": state.get("focused_index", 0),
|
|
42
|
+
"visible_count": state.get("visible_count", SEG_DEFAULT_VISIBLE_COUNT),
|
|
43
|
+
"card_width": state.get("card_width", SEG_DEFAULT_CARD_WIDTH),
|
|
44
|
+
"history": state.get("history", []),
|
|
45
|
+
"is_auto_mode": state.get("is_auto_mode", False),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# %% ../../nbs/components/helpers.ipynb #d0e1f2a3
|
|
49
|
+
# Default values for alignment card stack (compact cards for VAD chunks)
|
|
50
|
+
ALIGN_DEFAULT_VISIBLE_COUNT = 5
|
|
51
|
+
ALIGN_DEFAULT_CARD_WIDTH = 40
|
|
52
|
+
|
|
53
|
+
# %% ../../nbs/components/helpers.ipynb #e1f2a3b4
|
|
54
|
+
def extract_alignment_state(
|
|
55
|
+
ctx:InteractionContext, # Interaction context with state
|
|
56
|
+
) -> Dict[str, Any]: # Extracted state values
|
|
57
|
+
"""Extract alignment state as explicit values for renderers."""
|
|
58
|
+
state = ctx.state.get("step_states", {}).get("alignment", {})
|
|
59
|
+
return {
|
|
60
|
+
"is_initialized": state.get("is_initialized", False),
|
|
61
|
+
"vad_chunks": [VADChunk.from_dict(c) for c in state.get("vad_chunks", [])],
|
|
62
|
+
"focused_index": state.get("focused_chunk_index", 0),
|
|
63
|
+
"visible_count": state.get("visible_count", ALIGN_DEFAULT_VISIBLE_COUNT),
|
|
64
|
+
"card_width": state.get("card_width", ALIGN_DEFAULT_CARD_WIDTH),
|
|
65
|
+
"media_path": state.get("media_path"),
|
|
66
|
+
"media_paths": state.get("media_paths", []),
|
|
67
|
+
"audio_duration": state.get("audio_duration"),
|
|
68
|
+
"is_auto_mode": state.get("is_auto_mode", False),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# %% ../../nbs/components/helpers.ipynb #a3b4c5d6
|
|
72
|
+
def get_segment_count(
|
|
73
|
+
ctx:InteractionContext, # Interaction context with state
|
|
74
|
+
) -> int: # Number of segments
|
|
75
|
+
"""Get segment count from state without full extraction."""
|
|
76
|
+
state = ctx.state.get("step_states", {}).get("segmentation", {})
|
|
77
|
+
return len(state.get("segments", []))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_chunk_count(
|
|
81
|
+
ctx:InteractionContext, # Interaction context with state
|
|
82
|
+
) -> int: # Number of VAD chunks
|
|
83
|
+
"""Get VAD chunk count from state without full extraction."""
|
|
84
|
+
state = ctx.state.get("step_states", {}).get("alignment", {})
|
|
85
|
+
return len(state.get("vad_chunks", []))
|