@pipedog/core 0.0.0 → 0.2.0
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.
- package/LICENSE +25 -0
- package/NOTICE +9 -0
- package/dist/adapters/base_llm_adapter.d.ts +266 -0
- package/dist/adapters/base_llm_adapter.js +489 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +8 -0
- package/dist/adapters/schemas/direct_function.d.ts +202 -0
- package/dist/adapters/schemas/direct_function.js +302 -0
- package/dist/adapters/schemas/function_schema.d.ts +67 -0
- package/dist/adapters/schemas/function_schema.js +72 -0
- package/dist/adapters/schemas/index.d.ts +2 -0
- package/dist/adapters/schemas/index.js +8 -0
- package/dist/adapters/schemas/tools_schema.d.ts +57 -0
- package/dist/adapters/schemas/tools_schema.js +79 -0
- package/dist/adapters/services/index.d.ts +2 -0
- package/dist/adapters/services/index.js +8 -0
- package/dist/adapters/services/open_ai_adapter.d.ts +147 -0
- package/dist/adapters/services/open_ai_adapter.js +250 -0
- package/dist/audio/dtmf/dtmf_data.d.ts +3 -0
- package/dist/audio/dtmf/dtmf_data.js +22 -0
- package/dist/audio/dtmf/index.d.ts +2 -0
- package/dist/audio/dtmf/index.js +8 -0
- package/dist/audio/dtmf/types.d.ts +31 -0
- package/dist/audio/dtmf/types.js +35 -0
- package/dist/audio/dtmf/utils.d.ts +13 -0
- package/dist/audio/dtmf/utils.js +62 -0
- package/dist/audio/filters/base_audio_filter.d.ts +51 -0
- package/dist/audio/filters/base_audio_filter.js +17 -0
- package/dist/audio/filters/index.d.ts +2 -0
- package/dist/audio/filters/index.js +8 -0
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.js +8 -0
- package/dist/audio/krisp_instance.d.ts +95 -0
- package/dist/audio/krisp_instance.js +229 -0
- package/dist/audio/mixers/base_audio_mixer.d.ts +56 -0
- package/dist/audio/mixers/base_audio_mixer.js +16 -0
- package/dist/audio/mixers/index.d.ts +2 -0
- package/dist/audio/mixers/index.js +8 -0
- package/dist/audio/resamplers/base_audio_resampler.d.ts +46 -0
- package/dist/audio/resamplers/base_audio_resampler.js +35 -0
- package/dist/audio/resamplers/engine.d.ts +60 -0
- package/dist/audio/resamplers/engine.js +89 -0
- package/dist/audio/resamplers/index.d.ts +2 -0
- package/dist/audio/resamplers/index.js +8 -0
- package/dist/audio/resamplers/resampy_resampler.d.ts +10 -0
- package/dist/audio/resamplers/resampy_resampler.js +15 -0
- package/dist/audio/resamplers/sinc_engine.d.ts +10 -0
- package/dist/audio/resamplers/sinc_engine.js +235 -0
- package/dist/audio/resamplers/soxr_resampler.d.ts +58 -0
- package/dist/audio/resamplers/soxr_resampler.js +79 -0
- package/dist/audio/resamplers/soxr_stream_resampler.d.ts +78 -0
- package/dist/audio/resamplers/soxr_stream_resampler.js +159 -0
- package/dist/audio/turn/base_turn_analyzer.d.ts +84 -0
- package/dist/audio/turn/base_turn_analyzer.js +61 -0
- package/dist/audio/turn/index.d.ts +2 -0
- package/dist/audio/turn/index.js +8 -0
- package/dist/audio/turn/smart_turn/_whisper_features.d.ts +30 -0
- package/dist/audio/turn/smart_turn/_whisper_features.js +225 -0
- package/dist/audio/turn/smart_turn/base_smart_turn.d.ts +94 -0
- package/dist/audio/turn/smart_turn/base_smart_turn.js +239 -0
- package/dist/audio/turn/smart_turn/local_smart_turn_v3.d.ts +76 -0
- package/dist/audio/turn/smart_turn/local_smart_turn_v3.js +151 -0
- package/dist/audio/utils.d.ts +224 -0
- package/dist/audio/utils.js +634 -0
- package/dist/audio/vad/data/index.d.ts +2 -0
- package/dist/audio/vad/data/index.js +8 -0
- package/dist/audio/vad/index.d.ts +2 -0
- package/dist/audio/vad/index.js +8 -0
- package/dist/audio/vad/vad_analyzer.d.ts +134 -0
- package/dist/audio/vad/vad_analyzer.js +249 -0
- package/dist/audio/vad/vad_controller.d.ts +143 -0
- package/dist/audio/vad/vad_controller.js +228 -0
- package/dist/audio/volume.d.ts +38 -0
- package/dist/audio/volume.js +75 -0
- package/dist/bus/adapters/base.d.ts +35 -0
- package/dist/bus/adapters/base.js +19 -0
- package/dist/bus/adapters/index.d.ts +10 -0
- package/dist/bus/adapters/index.js +15 -0
- package/dist/bus/adapters/llm_context_adapter.d.ts +36 -0
- package/dist/bus/adapters/llm_context_adapter.js +82 -0
- package/dist/bus/adapters/tools_schema_adapter.d.ts +25 -0
- package/dist/bus/adapters/tools_schema_adapter.js +70 -0
- package/dist/bus/bridge_processor.d.ts +111 -0
- package/dist/bus/bridge_processor.js +166 -0
- package/dist/bus/bus.d.ts +110 -0
- package/dist/bus/bus.js +200 -0
- package/dist/bus/index.d.ts +22 -0
- package/dist/bus/index.js +27 -0
- package/dist/bus/local/async_queue.d.ts +12 -0
- package/dist/bus/local/async_queue.js +21 -0
- package/dist/bus/local/index.d.ts +3 -0
- package/dist/bus/local/index.js +8 -0
- package/dist/bus/messages.d.ts +378 -0
- package/dist/bus/messages.js +357 -0
- package/dist/bus/network/index.d.ts +16 -0
- package/dist/bus/network/index.js +21 -0
- package/dist/bus/queue.d.ts +27 -0
- package/dist/bus/queue.js +51 -0
- package/dist/bus/serializers/base.d.ts +25 -0
- package/dist/bus/serializers/base.js +14 -0
- package/dist/bus/serializers/index.d.ts +9 -0
- package/dist/bus/serializers/index.js +14 -0
- package/dist/bus/serializers/json.d.ts +77 -0
- package/dist/bus/serializers/json.js +367 -0
- package/dist/bus/subscriber.d.ts +46 -0
- package/dist/bus/subscriber.js +27 -0
- package/dist/bus/ui/index.d.ts +3 -0
- package/dist/bus/ui/index.js +8 -0
- package/dist/bus/ui/messages.d.ts +186 -0
- package/dist/bus/ui/messages.js +171 -0
- package/dist/classifiers/base_classifier.d.ts +263 -0
- package/dist/classifiers/base_classifier.js +346 -0
- package/dist/classifiers/cloudflare/clef/classifier.d.ts +70 -0
- package/dist/classifiers/cloudflare/clef/classifier.js +204 -0
- package/dist/classifiers/cloudflare/clef/client.d.ts +104 -0
- package/dist/classifiers/cloudflare/clef/client.js +234 -0
- package/dist/classifiers/cloudflare/clef/index.d.ts +2 -0
- package/dist/classifiers/cloudflare/clef/index.js +8 -0
- package/dist/classifiers/cloudflare/index.d.ts +2 -0
- package/dist/classifiers/cloudflare/index.js +8 -0
- package/dist/classifiers/http_client.d.ts +70 -0
- package/dist/classifiers/http_client.js +161 -0
- package/dist/classifiers/index.d.ts +2 -0
- package/dist/classifiers/index.js +8 -0
- package/dist/classifiers/jev/classifier.d.ts +8 -0
- package/dist/classifiers/jev/classifier.js +13 -0
- package/dist/classifiers/jev/client.d.ts +8 -0
- package/dist/classifiers/jev/client.js +13 -0
- package/dist/classifiers/jev/index.d.ts +2 -0
- package/dist/classifiers/jev/index.js +8 -0
- package/dist/classifiers/llm/classifier.d.ts +72 -0
- package/dist/classifiers/llm/classifier.js +304 -0
- package/dist/classifiers/llm/index.d.ts +2 -0
- package/dist/classifiers/llm/index.js +8 -0
- package/dist/classifiers/openai/decisions/classifier.d.ts +77 -0
- package/dist/classifiers/openai/decisions/classifier.js +241 -0
- package/dist/classifiers/openai/decisions/client.d.ts +108 -0
- package/dist/classifiers/openai/decisions/client.js +234 -0
- package/dist/classifiers/openai/decisions/index.d.ts +2 -0
- package/dist/classifiers/openai/decisions/index.js +8 -0
- package/dist/classifiers/openai/index.d.ts +2 -0
- package/dist/classifiers/openai/index.js +8 -0
- package/dist/classifiers/typesafe/index.d.ts +2 -0
- package/dist/classifiers/typesafe/index.js +8 -0
- package/dist/classifiers/typesafe/jev/classifier.d.ts +70 -0
- package/dist/classifiers/typesafe/jev/classifier.js +220 -0
- package/dist/classifiers/typesafe/jev/client.d.ts +105 -0
- package/dist/classifiers/typesafe/jev/client.js +233 -0
- package/dist/classifiers/typesafe/jev/index.d.ts +2 -0
- package/dist/classifiers/typesafe/jev/index.js +8 -0
- package/dist/clocks/base_clock.d.ts +13 -0
- package/dist/clocks/base_clock.js +14 -0
- package/dist/clocks/index.d.ts +2 -0
- package/dist/clocks/index.js +8 -0
- package/dist/clocks/system_clock.d.ts +15 -0
- package/dist/clocks/system_clock.js +25 -0
- package/dist/frames/frames.d.ts +2533 -0
- package/dist/frames/frames.js +2485 -0
- package/dist/frames/index.d.ts +2 -0
- package/dist/frames/index.js +8 -0
- package/dist/frames/protobufs/frames_pb2.d.ts +229 -0
- package/dist/frames/protobufs/frames_pb2.js +466 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +56 -0
- package/dist/metrics/index.d.ts +2 -0
- package/dist/metrics/index.js +8 -0
- package/dist/metrics/metrics.d.ts +276 -0
- package/dist/metrics/metrics.js +254 -0
- package/dist/observers/base_observer.d.ts +173 -0
- package/dist/observers/base_observer.js +151 -0
- package/dist/observers/error_observer.d.ts +75 -0
- package/dist/observers/error_observer.js +87 -0
- package/dist/observers/function_call_observer.d.ts +157 -0
- package/dist/observers/function_call_observer.js +222 -0
- package/dist/observers/index.d.ts +2 -0
- package/dist/observers/index.js +8 -0
- package/dist/observers/loggers/debug_log_observer.d.ts +91 -0
- package/dist/observers/loggers/debug_log_observer.js +228 -0
- package/dist/observers/loggers/index.d.ts +2 -0
- package/dist/observers/loggers/index.js +8 -0
- package/dist/observers/loggers/llm_log_observer.d.ts +24 -0
- package/dist/observers/loggers/llm_log_observer.js +110 -0
- package/dist/observers/loggers/metrics_log_observer.d.ts +85 -0
- package/dist/observers/loggers/metrics_log_observer.js +186 -0
- package/dist/observers/loggers/transcription_log_observer.d.ts +23 -0
- package/dist/observers/loggers/transcription_log_observer.js +74 -0
- package/dist/observers/service_metrics_observer.d.ts +168 -0
- package/dist/observers/service_metrics_observer.js +236 -0
- package/dist/observers/speaking_observer.d.ts +104 -0
- package/dist/observers/speaking_observer.js +150 -0
- package/dist/observers/startup_timing_observer.d.ts +221 -0
- package/dist/observers/startup_timing_observer.js +365 -0
- package/dist/observers/turn_tracking_observer.d.ts +78 -0
- package/dist/observers/turn_tracking_observer.js +189 -0
- package/dist/observers/user_bot_latency_observer.d.ts +411 -0
- package/dist/observers/user_bot_latency_observer.js +974 -0
- package/dist/pipeline/base_pipeline.d.ts +49 -0
- package/dist/pipeline/base_pipeline.js +101 -0
- package/dist/pipeline/capabilities.d.ts +76 -0
- package/dist/pipeline/capabilities.js +97 -0
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/pipeline/index.js +8 -0
- package/dist/pipeline/job_context.d.ts +317 -0
- package/dist/pipeline/job_context.js +450 -0
- package/dist/pipeline/job_decorator.d.ts +54 -0
- package/dist/pipeline/job_decorator.js +92 -0
- package/dist/pipeline/llm_switcher.d.ts +84 -0
- package/dist/pipeline/llm_switcher.js +107 -0
- package/dist/pipeline/llm_with_backend.d.ts +435 -0
- package/dist/pipeline/llm_with_backend.js +806 -0
- package/dist/pipeline/parallel_pipeline.d.ts +83 -0
- package/dist/pipeline/parallel_pipeline.js +200 -0
- package/dist/pipeline/pipeline.d.ts +126 -0
- package/dist/pipeline/pipeline.js +196 -0
- package/dist/pipeline/service_switcher.d.ts +287 -0
- package/dist/pipeline/service_switcher.js +509 -0
- package/dist/pipeline/sync_parallel_pipeline.d.ts +192 -0
- package/dist/pipeline/sync_parallel_pipeline.js +387 -0
- package/dist/pipeline/worker.d.ts +821 -0
- package/dist/pipeline/worker.js +1656 -0
- package/dist/pipeline/worker_observer.d.ts +131 -0
- package/dist/pipeline/worker_observer.js +274 -0
- package/dist/pipeline/worker_ready_decorator.d.ts +45 -0
- package/dist/pipeline/worker_ready_decorator.js +76 -0
- package/dist/processors/aggregators/async_tool_messages.d.ts +146 -0
- package/dist/processors/aggregators/async_tool_messages.js +291 -0
- package/dist/processors/aggregators/dtmf_aggregator.d.ts +61 -0
- package/dist/processors/aggregators/dtmf_aggregator.js +144 -0
- package/dist/processors/aggregators/gated.d.ts +43 -0
- package/dist/processors/aggregators/gated.js +86 -0
- package/dist/processors/aggregators/gated_llm_context.d.ts +42 -0
- package/dist/processors/aggregators/gated_llm_context.js +86 -0
- package/dist/processors/aggregators/index.d.ts +2 -0
- package/dist/processors/aggregators/index.js +8 -0
- package/dist/processors/aggregators/llm_context.d.ts +370 -0
- package/dist/processors/aggregators/llm_context.js +580 -0
- package/dist/processors/aggregators/llm_context_summarizer.d.ts +219 -0
- package/dist/processors/aggregators/llm_context_summarizer.js +447 -0
- package/dist/processors/aggregators/llm_response.d.ts +37 -0
- package/dist/processors/aggregators/llm_response.js +71 -0
- package/dist/processors/aggregators/llm_response_universal.d.ts +966 -0
- package/dist/processors/aggregators/llm_response_universal.js +2394 -0
- package/dist/processors/aggregators/llm_text_processor.d.ts +53 -0
- package/dist/processors/aggregators/llm_text_processor.js +102 -0
- package/dist/processors/aggregators/sentence.d.ts +39 -0
- package/dist/processors/aggregators/sentence.js +69 -0
- package/dist/processors/async_generator.d.ts +42 -0
- package/dist/processors/async_generator.js +76 -0
- package/dist/processors/audio/audio_buffer_processor.d.ts +241 -0
- package/dist/processors/audio/audio_buffer_processor.js +565 -0
- package/dist/processors/audio/index.d.ts +2 -0
- package/dist/processors/audio/index.js +8 -0
- package/dist/processors/audio/vad_processor.d.ts +67 -0
- package/dist/processors/audio/vad_processor.js +103 -0
- package/dist/processors/consumer_processor.d.ts +49 -0
- package/dist/processors/consumer_processor.js +85 -0
- package/dist/processors/filters/frame_filter.d.ts +39 -0
- package/dist/processors/filters/frame_filter.js +52 -0
- package/dist/processors/filters/function_filter.d.ts +59 -0
- package/dist/processors/filters/function_filter.js +78 -0
- package/dist/processors/filters/identity_filter.d.ts +33 -0
- package/dist/processors/filters/identity_filter.js +37 -0
- package/dist/processors/filters/index.d.ts +2 -0
- package/dist/processors/filters/index.js +8 -0
- package/dist/processors/filters/null_filter.d.ts +34 -0
- package/dist/processors/filters/null_filter.js +47 -0
- package/dist/processors/filters/wake_notifier_filter.d.ts +48 -0
- package/dist/processors/filters/wake_notifier_filter.js +49 -0
- package/dist/processors/frame_processor.d.ts +360 -0
- package/dist/processors/frame_processor.js +932 -0
- package/dist/processors/frameworks/index.d.ts +2 -0
- package/dist/processors/frameworks/index.js +8 -0
- package/dist/processors/frameworks/rtvi/frames.d.ts +241 -0
- package/dist/processors/frameworks/rtvi/frames.js +259 -0
- package/dist/processors/frameworks/rtvi/index.d.ts +6 -0
- package/dist/processors/frameworks/rtvi/index.js +11 -0
- package/dist/processors/frameworks/rtvi/models.d.ts +1741 -0
- package/dist/processors/frameworks/rtvi/models.js +1807 -0
- package/dist/processors/frameworks/rtvi/observer.d.ts +340 -0
- package/dist/processors/frameworks/rtvi/observer.js +1113 -0
- package/dist/processors/frameworks/rtvi/processor.d.ts +182 -0
- package/dist/processors/frameworks/rtvi/processor.js +676 -0
- package/dist/processors/idle_frame_processor.d.ts +44 -0
- package/dist/processors/idle_frame_processor.js +86 -0
- package/dist/processors/index.d.ts +2 -0
- package/dist/processors/index.js +8 -0
- package/dist/processors/logger.d.ts +42 -0
- package/dist/processors/logger.js +87 -0
- package/dist/processors/metrics/frame_processor_metrics.d.ts +172 -0
- package/dist/processors/metrics/frame_processor_metrics.js +372 -0
- package/dist/processors/metrics/index.d.ts +2 -0
- package/dist/processors/metrics/index.js +8 -0
- package/dist/processors/producer_processor.d.ts +68 -0
- package/dist/processors/producer_processor.js +78 -0
- package/dist/processors/text_transformer.d.ts +30 -0
- package/dist/processors/text_transformer.js +48 -0
- package/dist/registry/index.d.ts +3 -0
- package/dist/registry/index.js +8 -0
- package/dist/registry/registry.d.ts +69 -0
- package/dist/registry/registry.js +149 -0
- package/dist/registry/types.d.ts +47 -0
- package/dist/registry/types.js +39 -0
- package/dist/serializers/base_serializer.d.ts +94 -0
- package/dist/serializers/base_serializer.js +82 -0
- package/dist/serializers/index.d.ts +2 -0
- package/dist/serializers/index.js +8 -0
- package/dist/services/ai_service.d.ts +145 -0
- package/dist/services/ai_service.js +262 -0
- package/dist/services/anthropic/index.d.ts +2 -0
- package/dist/services/anthropic/index.js +7 -0
- package/dist/services/assemblyai/index.d.ts +2 -0
- package/dist/services/assemblyai/index.js +7 -0
- package/dist/services/asyncai/index.d.ts +2 -0
- package/dist/services/asyncai/index.js +7 -0
- package/dist/services/aws/index.d.ts +2 -0
- package/dist/services/aws/index.js +7 -0
- package/dist/services/azure/index.d.ts +2 -0
- package/dist/services/azure/index.js +7 -0
- package/dist/services/baseten/index.d.ts +2 -0
- package/dist/services/baseten/index.js +7 -0
- package/dist/services/bland/index.d.ts +2 -0
- package/dist/services/bland/index.js +7 -0
- package/dist/services/camb/index.d.ts +2 -0
- package/dist/services/camb/index.js +7 -0
- package/dist/services/cartesia/index.d.ts +2 -0
- package/dist/services/cartesia/index.js +7 -0
- package/dist/services/cerebras/index.d.ts +2 -0
- package/dist/services/cerebras/index.js +7 -0
- package/dist/services/crusoe/index.d.ts +2 -0
- package/dist/services/crusoe/index.js +7 -0
- package/dist/services/deepgram/index.d.ts +2 -0
- package/dist/services/deepgram/index.js +7 -0
- package/dist/services/deepseek/index.d.ts +2 -0
- package/dist/services/deepseek/index.js +7 -0
- package/dist/services/elevenlabs/index.d.ts +2 -0
- package/dist/services/elevenlabs/index.js +7 -0
- package/dist/services/fal/index.d.ts +2 -0
- package/dist/services/fal/index.js +7 -0
- package/dist/services/fireworks/index.d.ts +2 -0
- package/dist/services/fireworks/index.js +7 -0
- package/dist/services/fish/index.d.ts +2 -0
- package/dist/services/fish/index.js +7 -0
- package/dist/services/funasr/index.d.ts +3 -0
- package/dist/services/funasr/index.js +8 -0
- package/dist/services/gladia/index.d.ts +2 -0
- package/dist/services/gladia/index.js +7 -0
- package/dist/services/google/index.d.ts +2 -0
- package/dist/services/google/index.js +7 -0
- package/dist/services/gradium/index.d.ts +2 -0
- package/dist/services/gradium/index.js +7 -0
- package/dist/services/grok/index.d.ts +2 -0
- package/dist/services/grok/index.js +7 -0
- package/dist/services/groq/index.d.ts +2 -0
- package/dist/services/groq/index.js +7 -0
- package/dist/services/heygen/index.d.ts +2 -0
- package/dist/services/heygen/index.js +7 -0
- package/dist/services/hume/index.d.ts +2 -0
- package/dist/services/hume/index.js +7 -0
- package/dist/services/image_service.d.ts +52 -0
- package/dist/services/image_service.js +60 -0
- package/dist/services/inception/index.d.ts +2 -0
- package/dist/services/inception/index.js +7 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +8 -0
- package/dist/services/inworld/index.d.ts +2 -0
- package/dist/services/inworld/index.js +7 -0
- package/dist/services/keenable/index.d.ts +2 -0
- package/dist/services/keenable/index.js +7 -0
- package/dist/services/kokoro/index.d.ts +2 -0
- package/dist/services/kokoro/index.js +7 -0
- package/dist/services/llm_service.d.ts +1106 -0
- package/dist/services/llm_service.js +2231 -0
- package/dist/services/mem0/index.d.ts +2 -0
- package/dist/services/mem0/index.js +7 -0
- package/dist/services/meta/index.d.ts +2 -0
- package/dist/services/meta/index.js +7 -0
- package/dist/services/minimax/index.d.ts +2 -0
- package/dist/services/minimax/index.js +7 -0
- package/dist/services/mistral/index.d.ts +2 -0
- package/dist/services/mistral/index.js +7 -0
- package/dist/services/moondream/index.d.ts +2 -0
- package/dist/services/moondream/index.js +7 -0
- package/dist/services/moonshine/index.d.ts +2 -0
- package/dist/services/moonshine/index.js +7 -0
- package/dist/services/nebius/index.d.ts +2 -0
- package/dist/services/nebius/index.js +7 -0
- package/dist/services/neuphonic/index.d.ts +2 -0
- package/dist/services/neuphonic/index.js +7 -0
- package/dist/services/novita/index.d.ts +2 -0
- package/dist/services/novita/index.js +7 -0
- package/dist/services/nvidia/index.d.ts +2 -0
- package/dist/services/nvidia/index.js +11 -0
- package/dist/services/ollama/index.d.ts +2 -0
- package/dist/services/ollama/index.js +7 -0
- package/dist/services/openai/index.d.ts +2 -0
- package/dist/services/openai/index.js +7 -0
- package/dist/services/openclaw/index.d.ts +14 -0
- package/dist/services/openclaw/index.js +19 -0
- package/dist/services/openrouter/index.d.ts +2 -0
- package/dist/services/openrouter/index.js +7 -0
- package/dist/services/perplexity/index.d.ts +2 -0
- package/dist/services/perplexity/index.js +7 -0
- package/dist/services/piper/index.d.ts +2 -0
- package/dist/services/piper/index.js +7 -0
- package/dist/services/pocket_tts/index.d.ts +2 -0
- package/dist/services/pocket_tts/index.js +7 -0
- package/dist/services/qwen/index.d.ts +2 -0
- package/dist/services/qwen/index.js +7 -0
- package/dist/services/resembleai/index.d.ts +2 -0
- package/dist/services/resembleai/index.js +7 -0
- package/dist/services/rime/index.d.ts +2 -0
- package/dist/services/rime/index.js +7 -0
- package/dist/services/sambanova/index.d.ts +2 -0
- package/dist/services/sambanova/index.js +7 -0
- package/dist/services/sarvam/index.d.ts +2 -0
- package/dist/services/sarvam/index.js +7 -0
- package/dist/services/settings.d.ts +356 -0
- package/dist/services/settings.js +437 -0
- package/dist/services/simli/index.d.ts +2 -0
- package/dist/services/simli/index.js +7 -0
- package/dist/services/smallest/index.d.ts +2 -0
- package/dist/services/smallest/index.js +7 -0
- package/dist/services/soniox/index.d.ts +2 -0
- package/dist/services/soniox/index.js +7 -0
- package/dist/services/speechify/index.d.ts +2 -0
- package/dist/services/speechify/index.js +7 -0
- package/dist/services/speechmatics/index.d.ts +2 -0
- package/dist/services/speechmatics/index.js +7 -0
- package/dist/services/stt_latency.d.ts +62 -0
- package/dist/services/stt_latency.js +71 -0
- package/dist/services/stt_service.d.ts +601 -0
- package/dist/services/stt_service.js +1044 -0
- package/dist/services/tavus/index.d.ts +2 -0
- package/dist/services/tavus/index.js +7 -0
- package/dist/services/together/index.d.ts +2 -0
- package/dist/services/together/index.js +7 -0
- package/dist/services/tts_service.d.ts +909 -0
- package/dist/services/tts_service.js +2018 -0
- package/dist/services/ultravox/index.d.ts +2 -0
- package/dist/services/ultravox/index.js +9 -0
- package/dist/services/vision_service.d.ts +56 -0
- package/dist/services/vision_service.js +62 -0
- package/dist/services/websocket_service.d.ts +213 -0
- package/dist/services/websocket_service.js +654 -0
- package/dist/services/whisper/index.d.ts +2 -0
- package/dist/services/whisper/index.js +9 -0
- package/dist/services/xai/index.d.ts +2 -0
- package/dist/services/xai/index.js +9 -0
- package/dist/services/xtts/index.d.ts +2 -0
- package/dist/services/xtts/index.js +9 -0
- package/dist/tests/index.d.ts +2 -0
- package/dist/tests/index.js +8 -0
- package/dist/tests/utils.d.ts +123 -0
- package/dist/tests/utils.js +212 -0
- package/dist/transcriptions/index.d.ts +2 -0
- package/dist/transcriptions/index.js +8 -0
- package/dist/transcriptions/language.d.ts +376 -0
- package/dist/transcriptions/language.js +543 -0
- package/dist/transports/base_input.d.ts +149 -0
- package/dist/transports/base_input.js +338 -0
- package/dist/transports/base_output.d.ts +432 -0
- package/dist/transports/base_output.js +1231 -0
- package/dist/transports/base_transport.d.ts +223 -0
- package/dist/transports/base_transport.js +167 -0
- package/dist/transports/daily/index.d.ts +2 -0
- package/dist/transports/daily/index.js +9 -0
- package/dist/transports/heygen/index.d.ts +2 -0
- package/dist/transports/heygen/index.js +9 -0
- package/dist/transports/index.d.ts +2 -0
- package/dist/transports/index.js +8 -0
- package/dist/transports/lemonslice/index.d.ts +2 -0
- package/dist/transports/lemonslice/index.js +9 -0
- package/dist/transports/livekit/index.d.ts +2 -0
- package/dist/transports/livekit/index.js +9 -0
- package/dist/transports/local/index.d.ts +2 -0
- package/dist/transports/local/index.js +9 -0
- package/dist/transports/moq/index.d.ts +2 -0
- package/dist/transports/moq/index.js +9 -0
- package/dist/transports/smallwebrtc/index.d.ts +2 -0
- package/dist/transports/smallwebrtc/index.js +9 -0
- package/dist/transports/tavus/index.d.ts +2 -0
- package/dist/transports/tavus/index.js +9 -0
- package/dist/transports/video_in_sampler.d.ts +157 -0
- package/dist/transports/video_in_sampler.js +258 -0
- package/dist/transports/vonage/index.d.ts +2 -0
- package/dist/transports/vonage/index.js +9 -0
- package/dist/transports/websocket/index.d.ts +2 -0
- package/dist/transports/websocket/index.js +9 -0
- package/dist/transports/whatsapp/index.d.ts +2 -0
- package/dist/transports/whatsapp/index.js +9 -0
- package/dist/turns/eager_end_of_turn_mixin.d.ts +70 -0
- package/dist/turns/eager_end_of_turn_mixin.js +122 -0
- package/dist/turns/empty_user_turn.d.ts +55 -0
- package/dist/turns/empty_user_turn.js +53 -0
- package/dist/turns/index.d.ts +2 -0
- package/dist/turns/index.js +8 -0
- package/dist/turns/speculation_gate.d.ts +169 -0
- package/dist/turns/speculation_gate.js +294 -0
- package/dist/turns/types.d.ts +33 -0
- package/dist/turns/types.js +35 -0
- package/dist/turns/user_idle_controller.d.ts +94 -0
- package/dist/turns/user_idle_controller.js +187 -0
- package/dist/turns/user_mute/always_user_mute_strategy.d.ts +15 -0
- package/dist/turns/user_mute/always_user_mute_strategy.js +29 -0
- package/dist/turns/user_mute/base_user_mute_strategy.d.ts +39 -0
- package/dist/turns/user_mute/base_user_mute_strategy.js +45 -0
- package/dist/turns/user_mute/first_speech_user_mute_strategy.d.ts +28 -0
- package/dist/turns/user_mute/first_speech_user_mute_strategy.js +52 -0
- package/dist/turns/user_mute/function_call_user_mute_strategy.d.ts +25 -0
- package/dist/turns/user_mute/function_call_user_mute_strategy.js +50 -0
- package/dist/turns/user_mute/index.d.ts +6 -0
- package/dist/turns/user_mute/index.js +11 -0
- package/dist/turns/user_mute/mute_until_first_bot_complete_user_mute_strategy.d.ts +43 -0
- package/dist/turns/user_mute/mute_until_first_bot_complete_user_mute_strategy.js +76 -0
- package/dist/turns/user_start/base_user_turn_start_strategy.d.ts +135 -0
- package/dist/turns/user_start/base_user_turn_start_strategy.js +134 -0
- package/dist/turns/user_start/external_user_turn_start_strategy.d.ts +45 -0
- package/dist/turns/user_start/external_user_turn_start_strategy.js +64 -0
- package/dist/turns/user_start/index.d.ts +8 -0
- package/dist/turns/user_start/index.js +16 -0
- package/dist/turns/user_start/krisp_viva_ip_user_turn_start_strategy.d.ts +133 -0
- package/dist/turns/user_start/krisp_viva_ip_user_turn_start_strategy.js +243 -0
- package/dist/turns/user_start/min_words_user_turn_start_strategy.d.ts +60 -0
- package/dist/turns/user_start/min_words_user_turn_start_strategy.js +102 -0
- package/dist/turns/user_start/transcription_user_turn_start_strategy.d.ts +29 -0
- package/dist/turns/user_start/transcription_user_turn_start_strategy.js +43 -0
- package/dist/turns/user_start/vad_user_turn_start_strategy.d.ts +20 -0
- package/dist/turns/user_start/vad_user_turn_start_strategy.js +31 -0
- package/dist/turns/user_start/wake_phrase_user_turn_start_strategy.d.ts +141 -0
- package/dist/turns/user_start/wake_phrase_user_turn_start_strategy.js +238 -0
- package/dist/turns/user_stop/base_user_turn_stop_strategy.d.ts +174 -0
- package/dist/turns/user_stop/base_user_turn_stop_strategy.js +170 -0
- package/dist/turns/user_stop/deferred_user_turn_stop_strategy.d.ts +81 -0
- package/dist/turns/user_stop/deferred_user_turn_stop_strategy.js +107 -0
- package/dist/turns/user_stop/eager_match_policy.d.ts +36 -0
- package/dist/turns/user_stop/eager_match_policy.js +50 -0
- package/dist/turns/user_stop/eager_user_turn_stop_strategy.d.ts +84 -0
- package/dist/turns/user_stop/eager_user_turn_stop_strategy.js +154 -0
- package/dist/turns/user_stop/external_user_turn_completion_stop_strategy.d.ts +45 -0
- package/dist/turns/user_stop/external_user_turn_completion_stop_strategy.js +55 -0
- package/dist/turns/user_stop/external_user_turn_stop_strategy.d.ts +115 -0
- package/dist/turns/user_stop/external_user_turn_stop_strategy.js +205 -0
- package/dist/turns/user_stop/index.d.ts +10 -0
- package/dist/turns/user_stop/index.js +15 -0
- package/dist/turns/user_stop/llm_turn_completion_user_turn_stop_strategy.d.ts +44 -0
- package/dist/turns/user_stop/llm_turn_completion_user_turn_stop_strategy.js +59 -0
- package/dist/turns/user_stop/speech_timeout_user_turn_stop_strategy.d.ts +140 -0
- package/dist/turns/user_stop/speech_timeout_user_turn_stop_strategy.js +303 -0
- package/dist/turns/user_stop/turn_analyzer_user_turn_stop_strategy.d.ts +135 -0
- package/dist/turns/user_stop/turn_analyzer_user_turn_stop_strategy.js +332 -0
- package/dist/turns/user_turn_completion_mixin.d.ts +147 -0
- package/dist/turns/user_turn_completion_mixin.js +591 -0
- package/dist/turns/user_turn_controller.d.ts +134 -0
- package/dist/turns/user_turn_controller.js +335 -0
- package/dist/turns/user_turn_processor.d.ts +87 -0
- package/dist/turns/user_turn_processor.js +180 -0
- package/dist/turns/user_turn_strategies.d.ts +187 -0
- package/dist/turns/user_turn_strategies.js +222 -0
- package/dist/utils/async_tool_cancellation.d.ts +35 -0
- package/dist/utils/async_tool_cancellation.js +78 -0
- package/dist/utils/asyncio/index.d.ts +2 -0
- package/dist/utils/asyncio/index.js +7 -0
- package/dist/utils/asyncio/task_manager.d.ts +49 -0
- package/dist/utils/asyncio/task_manager.js +105 -0
- package/dist/utils/base_object.d.ts +76 -0
- package/dist/utils/base_object.js +154 -0
- package/dist/utils/context/aggregated_frame_sequencer.d.ts +491 -0
- package/dist/utils/context/aggregated_frame_sequencer.js +803 -0
- package/dist/utils/context/audio_context_manager.d.ts +131 -0
- package/dist/utils/context/audio_context_manager.js +233 -0
- package/dist/utils/context/index.d.ts +2 -0
- package/dist/utils/context/index.js +7 -0
- package/dist/utils/context/llm_context_summarization.d.ts +258 -0
- package/dist/utils/context/llm_context_summarization.js +511 -0
- package/dist/utils/context/text_segment_map.d.ts +611 -0
- package/dist/utils/context/text_segment_map.js +1132 -0
- package/dist/utils/context/word_completion_tracker.d.ts +273 -0
- package/dist/utils/context/word_completion_tracker.js +441 -0
- package/dist/utils/context/word_timestamp_tracker.d.ts +72 -0
- package/dist/utils/context/word_timestamp_tracker.js +98 -0
- package/dist/utils/deprecation.d.ts +28 -0
- package/dist/utils/deprecation.js +75 -0
- package/dist/utils/env.d.ts +38 -0
- package/dist/utils/env.js +62 -0
- package/dist/utils/errors.d.ts +75 -0
- package/dist/utils/errors.js +202 -0
- package/dist/utils/file_resolver.d.ts +126 -0
- package/dist/utils/file_resolver.js +314 -0
- package/dist/utils/file_storage.d.ts +115 -0
- package/dist/utils/file_storage.js +178 -0
- package/dist/utils/frame_queue.d.ts +36 -0
- package/dist/utils/frame_queue.js +66 -0
- package/dist/utils/http.d.ts +39 -0
- package/dist/utils/http.js +53 -0
- package/dist/utils/image.d.ts +14 -0
- package/dist/utils/image.js +63 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/jpeg_encoder.d.ts +14 -0
- package/dist/utils/jpeg_encoder.js +315 -0
- package/dist/utils/log_config.d.ts +34 -0
- package/dist/utils/log_config.js +76 -0
- package/dist/utils/logger.d.ts +40 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/mixin.d.ts +14 -0
- package/dist/utils/mixin.js +63 -0
- package/dist/utils/network.d.ts +73 -0
- package/dist/utils/network.js +90 -0
- package/dist/utils/security/allowed_origins.d.ts +16 -0
- package/dist/utils/security/allowed_origins.js +34 -0
- package/dist/utils/security/index.d.ts +2 -0
- package/dist/utils/security/index.js +7 -0
- package/dist/utils/security/redaction.d.ts +27 -0
- package/dist/utils/security/redaction.js +123 -0
- package/dist/utils/security/ssrf.d.ts +79 -0
- package/dist/utils/security/ssrf.js +327 -0
- package/dist/utils/shared.d.ts +35 -0
- package/dist/utils/shared.js +136 -0
- package/dist/utils/startup.d.ts +17 -0
- package/dist/utils/startup.js +130 -0
- package/dist/utils/string.d.ts +116 -0
- package/dist/utils/string.js +460 -0
- package/dist/utils/sync/base_notifier.d.ts +25 -0
- package/dist/utils/sync/base_notifier.js +16 -0
- package/dist/utils/sync/event_notifier.d.ts +38 -0
- package/dist/utils/sync/event_notifier.js +51 -0
- package/dist/utils/sync/index.d.ts +2 -0
- package/dist/utils/sync/index.js +7 -0
- package/dist/utils/text/alnum_utils.d.ts +109 -0
- package/dist/utils/text/alnum_utils.js +262 -0
- package/dist/utils/text/base_text_aggregator.d.ts +146 -0
- package/dist/utils/text/base_text_aggregator.js +102 -0
- package/dist/utils/text/base_text_filter.d.ts +54 -0
- package/dist/utils/text/base_text_filter.js +50 -0
- package/dist/utils/text/index.d.ts +2 -0
- package/dist/utils/text/index.js +7 -0
- package/dist/utils/text/markdown_text_filter.d.ts +147 -0
- package/dist/utils/text/markdown_text_filter.js +522 -0
- package/dist/utils/text/markup_utils.d.ts +54 -0
- package/dist/utils/text/markup_utils.js +156 -0
- package/dist/utils/text/pattern_pair_aggregator.d.ts +251 -0
- package/dist/utils/text/pattern_pair_aggregator.js +478 -0
- package/dist/utils/text/phonemes.d.ts +60 -0
- package/dist/utils/text/phonemes.js +172 -0
- package/dist/utils/text/simple_text_aggregator.d.ts +135 -0
- package/dist/utils/text/simple_text_aggregator.js +254 -0
- package/dist/utils/text/skip_tags_aggregator.d.ts +85 -0
- package/dist/utils/text/skip_tags_aggregator.js +147 -0
- package/dist/utils/text/transforms/acronyms.d.ts +21 -0
- package/dist/utils/text/transforms/acronyms.js +31 -0
- package/dist/utils/text/transforms/currency.d.ts +16 -0
- package/dist/utils/text/transforms/currency.js +56 -0
- package/dist/utils/text/transforms/dates.d.ts +18 -0
- package/dist/utils/text/transforms/dates.js +102 -0
- package/dist/utils/text/transforms/email.d.ts +17 -0
- package/dist/utils/text/transforms/email.js +35 -0
- package/dist/utils/text/transforms/index.d.ts +37 -0
- package/dist/utils/text/transforms/index.js +42 -0
- package/dist/utils/text/transforms/numbers.d.ts +25 -0
- package/dist/utils/text/transforms/numbers.js +59 -0
- package/dist/utils/text/transforms/percentages.d.ts +16 -0
- package/dist/utils/text/transforms/percentages.js +32 -0
- package/dist/utils/text/transforms/phone.d.ts +21 -0
- package/dist/utils/text/transforms/phone.js +39 -0
- package/dist/utils/text/transforms/pronunciations.d.ts +65 -0
- package/dist/utils/text/transforms/pronunciations.js +112 -0
- package/dist/utils/text/transforms/replacements.d.ts +39 -0
- package/dist/utils/text/transforms/replacements.js +46 -0
- package/dist/utils/text/transforms/strip_markdown.d.ts +25 -0
- package/dist/utils/text/transforms/strip_markdown.js +60 -0
- package/dist/utils/text/transforms/units.d.ts +22 -0
- package/dist/utils/text/transforms/units.js +81 -0
- package/dist/utils/text/transforms/voice_formatter.d.ts +86 -0
- package/dist/utils/text/transforms/voice_formatter.js +98 -0
- package/dist/utils/text/unicode_digits.d.ts +3 -0
- package/dist/utils/text/unicode_digits.js +34 -0
- package/dist/utils/text/word_timestamp_utils.d.ts +26 -0
- package/dist/utils/text/word_timestamp_utils.js +52 -0
- package/dist/utils/time.d.ts +13 -0
- package/dist/utils/time.js +31 -0
- package/dist/utils/tracing/index.d.ts +3 -0
- package/dist/utils/tracing/index.js +8 -0
- package/dist/utils/tracing/service_attributes.d.ts +198 -0
- package/dist/utils/tracing/service_attributes.js +434 -0
- package/dist/utils/tracing/service_decorators.d.ts +214 -0
- package/dist/utils/tracing/service_decorators.js +1662 -0
- package/dist/utils/tracing/setup.d.ts +107 -0
- package/dist/utils/tracing/setup.js +160 -0
- package/dist/utils/tracing/tracing_context.d.ts +55 -0
- package/dist/utils/tracing/tracing_context.js +97 -0
- package/dist/utils/tracing/turn_trace_observer.d.ts +95 -0
- package/dist/utils/tracing/turn_trace_observer.js +233 -0
- package/dist/utils/types.d.ts +80 -0
- package/dist/utils/types.js +108 -0
- package/dist/utils/utils.d.ts +5 -0
- package/dist/utils/utils.js +22 -0
- package/dist/utils/yaml.d.ts +179 -0
- package/dist/utils/yaml.js +556 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.js +7 -0
- package/dist/workers/base_worker.d.ts +772 -0
- package/dist/workers/base_worker.js +1298 -0
- package/dist/workers/index.d.ts +2 -0
- package/dist/workers/index.js +8 -0
- package/dist/workers/llm/backend_llm_worker.d.ts +552 -0
- package/dist/workers/llm/backend_llm_worker.js +1055 -0
- package/dist/workers/llm/index.d.ts +6 -0
- package/dist/workers/llm/index.js +11 -0
- package/dist/workers/llm/llm_context_worker.d.ts +66 -0
- package/dist/workers/llm/llm_context_worker.js +72 -0
- package/dist/workers/llm/llm_worker.d.ts +180 -0
- package/dist/workers/llm/llm_worker.js +323 -0
- package/dist/workers/llm/tool_decorator.d.ts +68 -0
- package/dist/workers/llm/tool_decorator.js +60 -0
- package/dist/workers/proxy/index.d.ts +3 -0
- package/dist/workers/proxy/index.js +9 -0
- package/dist/workers/proxy/websocket/client.d.ts +110 -0
- package/dist/workers/proxy/websocket/client.js +196 -0
- package/dist/workers/proxy/websocket/index.d.ts +3 -0
- package/dist/workers/proxy/websocket/index.js +10 -0
- package/dist/workers/runner.d.ts +268 -0
- package/dist/workers/runner.js +489 -0
- package/dist/workers/ui/index.d.ts +22 -0
- package/dist/workers/ui/index.js +27 -0
- package/dist/workers/ui/ui_event_decorator.d.ts +43 -0
- package/dist/workers/ui/ui_event_decorator.js +75 -0
- package/dist/workers/ui/ui_job_context.d.ts +12 -0
- package/dist/workers/ui/ui_job_context.js +17 -0
- package/dist/workers/ui/ui_prompts.d.ts +21 -0
- package/dist/workers/ui/ui_prompts.js +41 -0
- package/dist/workers/ui/ui_tools.d.ts +32 -0
- package/dist/workers/ui/ui_tools.js +112 -0
- package/dist/workers/ui/ui_worker.d.ts +571 -0
- package/dist/workers/ui/ui_worker.js +1156 -0
- package/package.json +61 -3
- package/README.md +0 -5
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
// Copyright (c) 2024-2026, Daily
|
|
2
|
+
// Copyright (c) 2026, pipedog contributors
|
|
3
|
+
// SPDX-License-Identifier: BSD-2-Clause
|
|
4
|
+
//
|
|
5
|
+
// Ported from pipecat: src/pipecat/pipeline/llm_with_backend.py
|
|
6
|
+
/**
|
|
7
|
+
* An LLM with a backend: a conversational frontend delegating the rest.
|
|
8
|
+
*
|
|
9
|
+
* The frontend is any LLM service, text or speech-to-speech, and holds the
|
|
10
|
+
* conversation. The backend is a `BackendLLMWorker` running a heavier model
|
|
11
|
+
* with the tools, and does the work the frontend hands off. `LLMWithBackend`
|
|
12
|
+
* wraps the frontend so the pair drops into a pipeline where an LLM goes, and
|
|
13
|
+
* installs the tools that join them.
|
|
14
|
+
*
|
|
15
|
+
* The two exchange messages. The frontend's `delegate` tool puts a message to
|
|
16
|
+
* the backend and returns at once; what the backend has to say comes back as a
|
|
17
|
+
* stream of outputs, each appended to the frontend's conversation as a message
|
|
18
|
+
* marked `Backend:`, spoken or silent as the backend's flag says. A second
|
|
19
|
+
* request joins the first, and a correction changes or stops it. How a request
|
|
20
|
+
* is worded is the `BackendRequestStrategy`'s business, and the
|
|
21
|
+
* `BackendConnector` owns the tools, the session with the backend, and how
|
|
22
|
+
* each output is rendered into the conversation.
|
|
23
|
+
*/
|
|
24
|
+
import { Event, rethrowIfCancelled, TimeoutError, waitFor } from "@pipedog/asyncio";
|
|
25
|
+
import { FunctionSchema } from "../adapters/schemas/function_schema.js";
|
|
26
|
+
import { FunctionCallResultProperties, LLMMessagesAppendFrame } from "../frames/frames.js";
|
|
27
|
+
import { LLMSpecificMessage, } from "../processors/aggregators/llm_context.js";
|
|
28
|
+
import { RTVIServerMessageFrame } from "../processors/frameworks/rtvi/frames.js";
|
|
29
|
+
import { NotImplementedError, } from "../services/llm_service.js";
|
|
30
|
+
import { logger } from "../utils/logger.js";
|
|
31
|
+
import { _BackendSession, _renderExplicitRequest, _renderTranscriptRequest, BackendError, BackendIdle, BackendLLMWorker, BackendOutput, BackendToolCall, EXPLICIT_REQUEST_INSTRUCTION, TRANSCRIPT_REQUEST_INSTRUCTION, } from "../workers/llm/backend_llm_worker.js";
|
|
32
|
+
import { Pipeline } from "./pipeline.js";
|
|
33
|
+
/** Name of the tool the frontend calls to delegate. */
|
|
34
|
+
export const DELEGATE_TOOL_NAME = "delegate";
|
|
35
|
+
/** How a backend output is marked in the frontend's conversation. */
|
|
36
|
+
export const BACKEND_MESSAGE_PREFIX = "Backend: ";
|
|
37
|
+
/** How a backend reasoning summary is marked in the frontend's conversation. */
|
|
38
|
+
export const BACKEND_THOUGHT_PREFIX = "Backend (thinking): ";
|
|
39
|
+
/**
|
|
40
|
+
* How a backend note, text its model wrote without the mark that asks for the
|
|
41
|
+
* user to be told it, is marked in the frontend's conversation.
|
|
42
|
+
*/
|
|
43
|
+
export const BACKEND_NOTE_PREFIX = "Backend (note): ";
|
|
44
|
+
/** How a function call the backend is making is marked in the frontend's conversation. */
|
|
45
|
+
export const BACKEND_WORKING_PREFIX = "Backend (working): ";
|
|
46
|
+
/** Every mark a rendered backend message can open with. */
|
|
47
|
+
export const BACKEND_PREFIXES = [
|
|
48
|
+
BACKEND_MESSAGE_PREFIX,
|
|
49
|
+
BACKEND_THOUGHT_PREFIX,
|
|
50
|
+
BACKEND_NOTE_PREFIX,
|
|
51
|
+
BACKEND_WORKING_PREFIX,
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* Carried on each spoken backend message, so the frontend relays it on
|
|
55
|
+
* whatever turn it lands on, a user's turn included, where the standing
|
|
56
|
+
* instruction alone leaves it unsaid.
|
|
57
|
+
*/
|
|
58
|
+
export const RELAY_NOTE = "(Relay this to the user now, in your own words, once, even if the conversation has " +
|
|
59
|
+
"moved on. If the user has just said something you have not answered yet, answer that " +
|
|
60
|
+
"first and relay this at the end of the same reply.)";
|
|
61
|
+
/** Where the request strategy's own guidance goes in `FRONTEND_PAIRING_INSTRUCTION`. */
|
|
62
|
+
export const STRATEGY_PLACEHOLDER = "{strategy}";
|
|
63
|
+
/**
|
|
64
|
+
* How the frontend's model works with a backend, appended to its system
|
|
65
|
+
* instruction: when and how to delegate, that to the user there is one
|
|
66
|
+
* assistant, what to do after delegating, and what to do with the backend's
|
|
67
|
+
* messages. The request strategy's guidance (how the request is worded) goes
|
|
68
|
+
* where `STRATEGY_PLACEHOLDER` stands, inside the delegating paragraph. The
|
|
69
|
+
* frontend's own system instruction says what the backend is for.
|
|
70
|
+
* `new BackendConnector({ frontendPairingInstruction })` replaces it.
|
|
71
|
+
*
|
|
72
|
+
* The order of the paragraphs matters, and the placeholder's place inside the
|
|
73
|
+
* first one: with the one-assistant paragraph first, or the strategy's
|
|
74
|
+
* guidance in a paragraph of its own, gpt-4.1 answers a user's "never mind"
|
|
75
|
+
* with "I won't make any changes" and no delegate call in nearly every run,
|
|
76
|
+
* where this order delegates the stop every time.
|
|
77
|
+
*/
|
|
78
|
+
export const FRONTEND_PAIRING_INSTRUCTION = "DELEGATING: Delegate to the backend when any part of what the user asks needs a backend " +
|
|
79
|
+
"tool or careful reasoning, and whenever the user says anything about work already " +
|
|
80
|
+
"handed over: a change, a narrowing, or calling it off, in any words, even in passing. " +
|
|
81
|
+
"Whether that work goes on is not yours to decide: hand the message over and the " +
|
|
82
|
+
"backend decides. You cannot stop or change that work yourself, and nothing you say " +
|
|
83
|
+
"reaches the backend: only a delegate call does, so saying you will stop without one " +
|
|
84
|
+
"leaves the work running. A request that arrives while the backend is already working " +
|
|
85
|
+
"is delegated like any other. Do not delegate only when the message neither needs " +
|
|
86
|
+
"backend work nor concerns work already handed over: small talk, or a question you can " +
|
|
87
|
+
"answer from a result you already have, or when you need a brief clarification first. " +
|
|
88
|
+
"Delegate before giving any answer that depends on backend work, and do not guess the " +
|
|
89
|
+
"result while waiting. To delegate, call the delegate tool at once, in that same reply, " +
|
|
90
|
+
`and do the rest of the reply yourself around it. ${STRATEGY_PLACEHOLDER}\n\n` +
|
|
91
|
+
"ONE ASSISTANT: To the user there is one assistant, you: never mention the backend, " +
|
|
92
|
+
"delegation, a handoff or tools. Say what is being done as your own doing, such as \"I'm " +
|
|
93
|
+
'checking" or "I\'ve fixed it", never "the backend is checking". A stop or a change you ' +
|
|
94
|
+
"hand over is your own doing too: never say that you passed it along, relayed it, handed " +
|
|
95
|
+
"it over or told anyone.\n\n" +
|
|
96
|
+
"AFTER DELEGATING: Acknowledge briefly, without offering updates or asking whether to " +
|
|
97
|
+
"go ahead, and do whatever else the user asked that you can do yourself. The result says " +
|
|
98
|
+
"whether the backend was idle or already working; if it was working, your request joins " +
|
|
99
|
+
"that work. The backend sees nothing of the conversation but what you delegate, so every " +
|
|
100
|
+
"new request that needs it takes a delegate call of its own, even while it is still " +
|
|
101
|
+
"working on an earlier one; that includes the user saying yes to something you or the " +
|
|
102
|
+
"backend offered to do, and the user calling off or changing work already under way, " +
|
|
103
|
+
"which the backend keeps doing until you hand that over. Saying you will check " +
|
|
104
|
+
"or look into something is not doing it: " +
|
|
105
|
+
"the backend hears nothing until you call delegate, so a reply that promises backend " +
|
|
106
|
+
"work calls delegate in that same reply. When the user asks whether work is under way, " +
|
|
107
|
+
"look at the conversation: if you never delegated it, say it is not started yet and delegate " +
|
|
108
|
+
"it then. When you hand over a stop or a change, the backend stops or changes the work as " +
|
|
109
|
+
"told: tell the user plainly that you are stopping or changing it, and do not promise results " +
|
|
110
|
+
"from work the user called off.\n\n" +
|
|
111
|
+
"BACKEND MESSAGES: The backend works on its own after you delegate and may be doing " +
|
|
112
|
+
"several things at once. What it has to say arrives as messages in the conversation " +
|
|
113
|
+
`marked "${BACKEND_MESSAGE_PREFIX.trim()}": a result, a question for the user, or news ` +
|
|
114
|
+
"worth an update, on whatever turn happens to be in progress by then. Such a message is " +
|
|
115
|
+
"owed to the user, whatever the conversation has moved on to: small talk, other " +
|
|
116
|
+
"questions or a long wait do not make it unwanted. If the user has said something you " +
|
|
117
|
+
"have not answered yet, answer that first, then relay the message at the end of that " +
|
|
118
|
+
"same reply, in your own words. If you have already answered everything the user said, " +
|
|
119
|
+
"relay just the message; do not repeat or rephrase your earlier reply. Relay each " +
|
|
120
|
+
"message once. Only when the user has cancelled or changed what they asked for does a " +
|
|
121
|
+
"message go unsaid, and then say only what still helps. Messages marked " +
|
|
122
|
+
`"${BACKEND_WORKING_PREFIX.trim()}", "${BACKEND_NOTE_PREFIX.trim()}" and ` +
|
|
123
|
+
`"${BACKEND_THOUGHT_PREFIX.trim()}" are what the backend is doing and thinking. They ` +
|
|
124
|
+
"carry no results: what a call found reaches you only in a message marked " +
|
|
125
|
+
`"${BACKEND_MESSAGE_PREFIX.trim()}", so never describe or guess what a call found. Never ` +
|
|
126
|
+
"relay them on their own, but when the user asks how the work is going, answer from the " +
|
|
127
|
+
"latest of them, concretely: name the step, such as which file it is reading or that it " +
|
|
128
|
+
"is running the tests, rather than saying only that it is still working. Never state a " +
|
|
129
|
+
"result you have not received from the backend, and never say work is done that the " +
|
|
130
|
+
"backend has not said is done.";
|
|
131
|
+
/** The transcript strategy's guidance, in the pairing instruction's delegating paragraph. */
|
|
132
|
+
export const TRANSCRIPT_FRONTEND_INSTRUCTION = "A delegation hands over the whole conversation, not one item: the backend reads it and " +
|
|
133
|
+
"does everything in it that is its job, so delegate once per reply however many things " +
|
|
134
|
+
"the user asked for, and do not word the request.";
|
|
135
|
+
/** How the transcript strategy describes the `delegate` tool. */
|
|
136
|
+
export const TRANSCRIPT_TOOL_DESCRIPTION = "Hand the conversation over to the backend, which reads it and does everything in it " +
|
|
137
|
+
"that needs a backend tool or careful reasoning. Call this as soon as any part of what " +
|
|
138
|
+
"the user asks needs that, and do the rest yourself. One handoff per reply, however " +
|
|
139
|
+
"many things the user asked for: two questions, or one question about two places, is " +
|
|
140
|
+
"one handoff. The backend sees nothing but what is handed over, so a request the user " +
|
|
141
|
+
"makes after your last handoff needs a handoff of its own, even while the backend is " +
|
|
142
|
+
"still working, and so does anything the user says about work already handed over: a " +
|
|
143
|
+
"change, a narrowing, or calling it off, in any words. It takes no arguments. Keep " +
|
|
144
|
+
"talking with the user while it works.";
|
|
145
|
+
/** The explicit strategy's guidance, in the pairing instruction's delegating paragraph. */
|
|
146
|
+
export const EXPLICIT_FRONTEND_INSTRUCTION = "Word the request so it stands on its own: the user's goal, the exact " +
|
|
147
|
+
"details they gave and their latest correction, with everything new they asked for in " +
|
|
148
|
+
"the one request, so you delegate once per reply however many things that is. Work " +
|
|
149
|
+
"already handed over stays with the backend: a new request joins it, so do not restate " +
|
|
150
|
+
"earlier requests; a correction names what changes, and a stop names what to stop.";
|
|
151
|
+
/** How the explicit strategy describes the `delegate` tool. */
|
|
152
|
+
export const EXPLICIT_TOOL_DESCRIPTION = "Hand a request to the backend, which does everything in it that needs a backend tool " +
|
|
153
|
+
"or careful reasoning. Call this as soon as any part of what the user asks needs that, " +
|
|
154
|
+
"with the request worded to stand on its own, and do the rest yourself. One call per " +
|
|
155
|
+
"reply, however many things the user asked for: two questions, or one question about " +
|
|
156
|
+
"two places, go in one request. Work already handed over stays with the backend, so " +
|
|
157
|
+
"send only what is new, including anything the user says about that work, such as to " +
|
|
158
|
+
"stop or change it. Keep talking with the user while it works.";
|
|
159
|
+
/** Python's `repr()` of a string: single-quoted unless it holds only single quotes. */
|
|
160
|
+
function reprStr(s) {
|
|
161
|
+
const quote = s.includes("'") && !s.includes('"') ? '"' : "'";
|
|
162
|
+
let out = "";
|
|
163
|
+
for (const ch of s) {
|
|
164
|
+
if (ch === "\\")
|
|
165
|
+
out += "\\\\";
|
|
166
|
+
else if (ch === quote)
|
|
167
|
+
out += `\\${ch}`;
|
|
168
|
+
else if (ch === "\n")
|
|
169
|
+
out += "\\n";
|
|
170
|
+
else if (ch === "\r")
|
|
171
|
+
out += "\\r";
|
|
172
|
+
else if (ch === "\t")
|
|
173
|
+
out += "\\t";
|
|
174
|
+
else if (ch < " " || ch === "\x7f")
|
|
175
|
+
out += `\\x${ch.charCodeAt(0).toString(16).padStart(2, "0")}`;
|
|
176
|
+
else
|
|
177
|
+
out += ch;
|
|
178
|
+
}
|
|
179
|
+
return `${quote}${out}${quote}`;
|
|
180
|
+
}
|
|
181
|
+
/** Approximation of Python's `repr()` for log lines and rendered arguments. */
|
|
182
|
+
function repr(value) {
|
|
183
|
+
if (value === null || value === undefined)
|
|
184
|
+
return "None";
|
|
185
|
+
if (typeof value === "string")
|
|
186
|
+
return reprStr(value);
|
|
187
|
+
if (typeof value === "boolean")
|
|
188
|
+
return value ? "True" : "False";
|
|
189
|
+
if (typeof value === "object") {
|
|
190
|
+
try {
|
|
191
|
+
return JSON.stringify(value);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return String(value);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return String(value);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* What a connector needs to know about the layers it joins.
|
|
201
|
+
*
|
|
202
|
+
* - `backendName`: name of the backend worker, local or registered elsewhere.
|
|
203
|
+
* - `frontendIsRealtime`: whether the frontend is a speech-to-speech service,
|
|
204
|
+
* whose context can lag the audio, which changes how a delegation words its
|
|
205
|
+
* request.
|
|
206
|
+
*/
|
|
207
|
+
export class ConnectorContext {
|
|
208
|
+
backendName;
|
|
209
|
+
frontendIsRealtime;
|
|
210
|
+
constructor(options) {
|
|
211
|
+
this.backendName = options.backendName;
|
|
212
|
+
this.frontendIsRealtime = options.frontendIsRealtime;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Request strategies: frontend → backend
|
|
217
|
+
//
|
|
218
|
+
// Two ways for a delegate call to become the backend's request, chosen by
|
|
219
|
+
// frontend kind when the app gives none. TranscriptBackendRequestStrategy
|
|
220
|
+
// hands over the conversation since the previous delegation: the default for
|
|
221
|
+
// a text frontend, whose context is current when the tool runs.
|
|
222
|
+
// ExplicitBackendRequestStrategy has the frontend model word the request
|
|
223
|
+
// itself: the default for a speech-to-speech frontend, which responds to audio
|
|
224
|
+
// before the transcript of that audio reaches its context, so the turns that
|
|
225
|
+
// prompted a handoff may not be in the context yet, and a transcript taken
|
|
226
|
+
// then would miss them.
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
/**
|
|
229
|
+
* Defines the `delegate` tool the frontend calls, and turns a call into the
|
|
230
|
+
* request the backend receives.
|
|
231
|
+
*
|
|
232
|
+
* The tool's parameters are this strategy's to declare, so the connector's
|
|
233
|
+
* handler has one signature and reads whatever was declared back from
|
|
234
|
+
* `params.arguments` in `composeRequest`.
|
|
235
|
+
*/
|
|
236
|
+
export class BackendRequestStrategy {
|
|
237
|
+
/** JSON-schema properties of the `delegate` tool; empty when it takes none. */
|
|
238
|
+
toolParameters = {};
|
|
239
|
+
/** Which of the properties are required. */
|
|
240
|
+
toolRequired = [];
|
|
241
|
+
/**
|
|
242
|
+
* The strategy's guidance to the frontend model, if any: how the request is
|
|
243
|
+
* worded. It goes where the pairing instruction's `STRATEGY_PLACEHOLDER`
|
|
244
|
+
* stands.
|
|
245
|
+
*/
|
|
246
|
+
frontendInstruction = null;
|
|
247
|
+
/** How the `delegate` tool is described to the frontend model. */
|
|
248
|
+
toolDescription = "";
|
|
249
|
+
/**
|
|
250
|
+
* Whether the strategy reads the conversation from the frontend's context,
|
|
251
|
+
* so it needs that context to be current when the `delegate` tool runs. A
|
|
252
|
+
* speech-to-speech frontend's context lags the audio, so the connector
|
|
253
|
+
* refuses such a strategy there.
|
|
254
|
+
*/
|
|
255
|
+
requiresContext = false;
|
|
256
|
+
/**
|
|
257
|
+
* Turn a `delegate` call into the text put to the backend.
|
|
258
|
+
*
|
|
259
|
+
* @param params The call, carrying the arguments the tool declared and the
|
|
260
|
+
* frontend's context.
|
|
261
|
+
* @returns The request text, or null when the call carries nothing new for
|
|
262
|
+
* the backend, in which case nothing is sent.
|
|
263
|
+
*/
|
|
264
|
+
async composeRequest(_params) {
|
|
265
|
+
throw new NotImplementedError();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Hands the backend the conversation and lets it work out the request.
|
|
270
|
+
*
|
|
271
|
+
* The frontend model calls the tool with no arguments; the request is the
|
|
272
|
+
* transcript of what was said since the previous delegation (the whole
|
|
273
|
+
* conversation the first time), rendered by `_renderTranscriptRequest`. The
|
|
274
|
+
* backend's own messages in the conversation are left out: the backend
|
|
275
|
+
* already has what it said. The default for a text frontend, whose context is
|
|
276
|
+
* current when the tool runs.
|
|
277
|
+
*
|
|
278
|
+
* The cursor assumes the conversation accrues. A rewritten context or a failed
|
|
279
|
+
* delegation's turns are not re-sent; the user's next request carries what
|
|
280
|
+
* matters. A call that finds no new turn since the previous delegation sends
|
|
281
|
+
* nothing.
|
|
282
|
+
*/
|
|
283
|
+
export class TranscriptBackendRequestStrategy extends BackendRequestStrategy {
|
|
284
|
+
requiresContext = true;
|
|
285
|
+
_requestInstruction;
|
|
286
|
+
_delegatedThrough = 0;
|
|
287
|
+
/** Initialize the strategy. */
|
|
288
|
+
constructor(options = {}) {
|
|
289
|
+
super();
|
|
290
|
+
this.frontendInstruction =
|
|
291
|
+
options.frontendInstruction === undefined
|
|
292
|
+
? TRANSCRIPT_FRONTEND_INSTRUCTION
|
|
293
|
+
: options.frontendInstruction;
|
|
294
|
+
this.toolDescription = options.toolDescription ?? TRANSCRIPT_TOOL_DESCRIPTION;
|
|
295
|
+
this._requestInstruction = options.requestInstruction ?? TRANSCRIPT_REQUEST_INSTRUCTION;
|
|
296
|
+
}
|
|
297
|
+
/** Render the turns since the previous delegation as the request. */
|
|
298
|
+
async composeRequest(params) {
|
|
299
|
+
const messages = params.context.getMessages();
|
|
300
|
+
if (this._delegatedThrough > messages.length) {
|
|
301
|
+
// The context holds fewer messages than the previous delegation read:
|
|
302
|
+
// it was reset since, so the transcript starts from the top.
|
|
303
|
+
this._delegatedThrough = 0;
|
|
304
|
+
}
|
|
305
|
+
// An LLMSpecificMessage, in a service's own format, is not a user or
|
|
306
|
+
// assistant turn, so the transcript leaves it out.
|
|
307
|
+
const conversation = messages
|
|
308
|
+
.slice(this._delegatedThrough)
|
|
309
|
+
.filter((m) => isStandardMessage(m) && !_isBackendMessage(m));
|
|
310
|
+
const first = this._delegatedThrough === 0;
|
|
311
|
+
this._delegatedThrough = messages.length;
|
|
312
|
+
if (!conversation.some((m) => m.role === "user" || m.role === "assistant")) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
return _renderTranscriptRequest(conversation, {
|
|
316
|
+
instruction: this._requestInstruction,
|
|
317
|
+
first,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Has the frontend model word the request itself.
|
|
323
|
+
*
|
|
324
|
+
* The tool takes a `request` argument, which is sent to the backend as it
|
|
325
|
+
* stands. The default for a speech-to-speech frontend, which responds to audio
|
|
326
|
+
* before the transcript of that audio reaches its context, so the turns that
|
|
327
|
+
* prompted a handoff may not be recorded when the tool runs.
|
|
328
|
+
*/
|
|
329
|
+
export class ExplicitBackendRequestStrategy extends BackendRequestStrategy {
|
|
330
|
+
toolParameters = {
|
|
331
|
+
request: {
|
|
332
|
+
type: "string",
|
|
333
|
+
description: "What the user is asking for now, self-contained: their goal, the details " +
|
|
334
|
+
"they gave (places, dates, names) and their latest correction, or what they " +
|
|
335
|
+
"said about work already under way, such as to stop it or change it. Work " +
|
|
336
|
+
"already handed over stays with the backend; a new request joins it, so do " +
|
|
337
|
+
"not restate earlier requests.",
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
toolRequired = ["request"];
|
|
341
|
+
_requestInstruction;
|
|
342
|
+
/** Initialize the strategy. */
|
|
343
|
+
constructor(options = {}) {
|
|
344
|
+
super();
|
|
345
|
+
this.frontendInstruction =
|
|
346
|
+
options.frontendInstruction === undefined
|
|
347
|
+
? EXPLICIT_FRONTEND_INSTRUCTION
|
|
348
|
+
: options.frontendInstruction;
|
|
349
|
+
this.toolDescription = options.toolDescription ?? EXPLICIT_TOOL_DESCRIPTION;
|
|
350
|
+
this._requestInstruction = options.requestInstruction ?? EXPLICIT_REQUEST_INSTRUCTION;
|
|
351
|
+
}
|
|
352
|
+
/** Send the model's request as it stands, with the reporting expectation after it. */
|
|
353
|
+
async composeRequest(params) {
|
|
354
|
+
const raw = params.arguments.request;
|
|
355
|
+
const request = String(raw || "").trim();
|
|
356
|
+
if (!request) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
return _renderExplicitRequest(request, { instruction: this._requestInstruction });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/** Whether a context message is a plain (dict) message rather than an LLM-specific one. */
|
|
363
|
+
function isStandardMessage(message) {
|
|
364
|
+
return (message !== null && typeof message === "object" && !(message instanceof LLMSpecificMessage));
|
|
365
|
+
}
|
|
366
|
+
/** Whether a context message is one the connector rendered from a backend output. */
|
|
367
|
+
export function _isBackendMessage(message) {
|
|
368
|
+
if (!isStandardMessage(message)) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
const content = message.content;
|
|
372
|
+
return (message.role === "developer" &&
|
|
373
|
+
typeof content === "string" &&
|
|
374
|
+
BACKEND_PREFIXES.some((prefix) => content.startsWith(prefix)));
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Joins the two layers: the tools the frontend calls, the session with the
|
|
378
|
+
* backend, and what each output becomes.
|
|
379
|
+
*
|
|
380
|
+
* A request strategy defines the `delegate` tool and composes the request. It
|
|
381
|
+
* may be given, or left to the connector to pick by frontend kind once bound:
|
|
382
|
+
* `TranscriptBackendRequestStrategy` for a text frontend,
|
|
383
|
+
* `ExplicitBackendRequestStrategy` for a speech-to-speech one, whose context
|
|
384
|
+
* lags the audio. A given strategy that reads the frontend's context
|
|
385
|
+
* (`BackendRequestStrategy.requiresContext`) is refused for a speech-to-speech
|
|
386
|
+
* frontend.
|
|
387
|
+
*
|
|
388
|
+
* The connector holds the session with the backend for the frontend's life,
|
|
389
|
+
* and turns each output the backend sends into a message appended to the
|
|
390
|
+
* frontend's conversation with `renderOutput`, run or not as the output's
|
|
391
|
+
* `prefersSpoken` flag says. The function calls the backend makes on the way
|
|
392
|
+
* are reported in the frontend's pipeline for clients to show; nothing else in
|
|
393
|
+
* the pipeline sees them.
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* const connector = new BackendConnector({ respondOnDelegate: false });
|
|
397
|
+
*/
|
|
398
|
+
export class BackendConnector {
|
|
399
|
+
_requestStrategy;
|
|
400
|
+
_frontendPairingInstruction;
|
|
401
|
+
_respondOnDelegate;
|
|
402
|
+
_timeoutSecs;
|
|
403
|
+
_clientTrace;
|
|
404
|
+
_context = null;
|
|
405
|
+
_tools = [];
|
|
406
|
+
_session = null;
|
|
407
|
+
_sessionOpen = new Event();
|
|
408
|
+
/** Initialize the connector. */
|
|
409
|
+
constructor(options = {}) {
|
|
410
|
+
this._requestStrategy = options.requestStrategy ?? null;
|
|
411
|
+
this._frontendPairingInstruction =
|
|
412
|
+
options.frontendPairingInstruction ?? FRONTEND_PAIRING_INSTRUCTION;
|
|
413
|
+
this._respondOnDelegate = options.respondOnDelegate ?? true;
|
|
414
|
+
this._timeoutSecs = options.timeoutSecs === undefined ? 30 : options.timeoutSecs;
|
|
415
|
+
this._clientTrace = options.clientTrace ?? false;
|
|
416
|
+
}
|
|
417
|
+
/** The request strategy in use. Available once bound. */
|
|
418
|
+
get requestStrategy() {
|
|
419
|
+
if (this._requestStrategy === null)
|
|
420
|
+
throw new Error("connector not bound");
|
|
421
|
+
return this._requestStrategy;
|
|
422
|
+
}
|
|
423
|
+
/** The tools to install on the frontend. Available once bound. */
|
|
424
|
+
get tools() {
|
|
425
|
+
if (this._tools.length === 0)
|
|
426
|
+
throw new Error("connector not bound");
|
|
427
|
+
return this._tools;
|
|
428
|
+
}
|
|
429
|
+
/** The `delegate` tool. Available once bound. */
|
|
430
|
+
get tool() {
|
|
431
|
+
return this.tools[0];
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* What is appended to the frontend's system instruction: the pairing
|
|
435
|
+
* instruction with the strategy's guidance in place. Available once bound.
|
|
436
|
+
*/
|
|
437
|
+
get frontendInstruction() {
|
|
438
|
+
const pairing = this._frontendPairingInstruction;
|
|
439
|
+
const guidance = this.requestStrategy.frontendInstruction || "";
|
|
440
|
+
if (pairing.includes(STRATEGY_PLACEHOLDER)) {
|
|
441
|
+
return pairing.replaceAll(STRATEGY_PLACEHOLDER, guidance).replaceAll(" \n", "\n").trim();
|
|
442
|
+
}
|
|
443
|
+
return [pairing, guidance].filter((p) => p).join("\n\n") || null;
|
|
444
|
+
}
|
|
445
|
+
/** The session with the backend, while one is open. */
|
|
446
|
+
get session() {
|
|
447
|
+
return this._session;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Settle the strategy for the layers being joined and build the tools.
|
|
451
|
+
*
|
|
452
|
+
* @param context The layers.
|
|
453
|
+
* @throws Error If the frontend is speech-to-speech and the strategy reads
|
|
454
|
+
* the frontend's context, which lags the audio there.
|
|
455
|
+
*/
|
|
456
|
+
bind(context) {
|
|
457
|
+
this._context = context;
|
|
458
|
+
if (this._requestStrategy === null) {
|
|
459
|
+
this._requestStrategy = context.frontendIsRealtime
|
|
460
|
+
? new ExplicitBackendRequestStrategy()
|
|
461
|
+
: new TranscriptBackendRequestStrategy();
|
|
462
|
+
}
|
|
463
|
+
if (context.frontendIsRealtime && this._requestStrategy.requiresContext) {
|
|
464
|
+
throw new Error(`${this._requestStrategy.constructor.name} reads the conversation from the ` +
|
|
465
|
+
"frontend's context, which lags the audio on a speech-to-speech frontend. " +
|
|
466
|
+
"Use a request strategy that doesn't read the context, such as " +
|
|
467
|
+
"ExplicitBackendRequestStrategy.");
|
|
468
|
+
}
|
|
469
|
+
this._tools = this.buildTools();
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Build the tools to install on the frontend: `delegate`.
|
|
473
|
+
*
|
|
474
|
+
* Override to install tools of another shape entirely; the service reads
|
|
475
|
+
* nothing else from the connector but `frontendInstruction`.
|
|
476
|
+
*
|
|
477
|
+
* @returns The tools, each carrying its handler.
|
|
478
|
+
*/
|
|
479
|
+
buildTools() {
|
|
480
|
+
const delegate = async (params) => {
|
|
481
|
+
await this.delegate(params);
|
|
482
|
+
};
|
|
483
|
+
return [
|
|
484
|
+
new FunctionSchema({
|
|
485
|
+
name: DELEGATE_TOOL_NAME,
|
|
486
|
+
description: this.requestStrategy.toolDescription,
|
|
487
|
+
properties: this.requestStrategy.toolParameters,
|
|
488
|
+
required: this.requestStrategy.toolRequired,
|
|
489
|
+
handler: delegate,
|
|
490
|
+
}),
|
|
491
|
+
];
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Hold the session with the backend, delivering what it produces, until cancelled.
|
|
495
|
+
*
|
|
496
|
+
* @param worker The pipeline worker the frontend runs in.
|
|
497
|
+
* @param frontend The frontend service, which takes the deliveries.
|
|
498
|
+
*/
|
|
499
|
+
async runSession(worker, frontend) {
|
|
500
|
+
const context = this._context;
|
|
501
|
+
if (context === null)
|
|
502
|
+
throw new Error("connector not bound");
|
|
503
|
+
try {
|
|
504
|
+
const session = new _BackendSession(worker, context.backendName, {
|
|
505
|
+
timeoutSecs: this._timeoutSecs,
|
|
506
|
+
});
|
|
507
|
+
await session.run(async (session) => {
|
|
508
|
+
this._session = session;
|
|
509
|
+
this._sessionOpen.set();
|
|
510
|
+
for await (const event of session) {
|
|
511
|
+
await this.deliver(frontend, event);
|
|
512
|
+
}
|
|
513
|
+
if (!session.detached) {
|
|
514
|
+
logger.warning(`Backend '${context.backendName}' went away`);
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
catch (e) {
|
|
519
|
+
rethrowIfCancelled(e);
|
|
520
|
+
logger.error(`The session with backend '${context.backendName}' failed: ${e instanceof Error ? e.message : e}`);
|
|
521
|
+
}
|
|
522
|
+
finally {
|
|
523
|
+
this._session = null;
|
|
524
|
+
this._sessionOpen.clear();
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/** The session, once it is open; waits for it up to the timeout. */
|
|
528
|
+
async _openSession() {
|
|
529
|
+
if (this._session === null) {
|
|
530
|
+
try {
|
|
531
|
+
await waitFor(() => this._sessionOpen.wait(), this._timeoutSecs);
|
|
532
|
+
}
|
|
533
|
+
catch (e) {
|
|
534
|
+
if (e instanceof TimeoutError) {
|
|
535
|
+
throw new Error("the backend is not attached");
|
|
536
|
+
}
|
|
537
|
+
throw e;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (this._session === null)
|
|
541
|
+
throw new Error("the backend is not attached");
|
|
542
|
+
return this._session;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Put the request a `delegate` call carries to the backend, and settle the call at once.
|
|
546
|
+
*
|
|
547
|
+
* @param params The `delegate` call.
|
|
548
|
+
*/
|
|
549
|
+
async delegate(params) {
|
|
550
|
+
const context = this._context;
|
|
551
|
+
if (context === null)
|
|
552
|
+
throw new Error("connector not bound");
|
|
553
|
+
const request = await this.requestStrategy.composeRequest(params);
|
|
554
|
+
if (request === null) {
|
|
555
|
+
// No turns since the previous delegation: the backend already has
|
|
556
|
+
// everything the transcript would carry, so nothing is sent.
|
|
557
|
+
logger.debug(`Delegate call ${params.toolCallId} carries nothing new; not sent`);
|
|
558
|
+
await params.resultCallback({ status: "already_delegated" }, { properties: new FunctionCallResultProperties({ runLlm: false }) });
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
logger.debug(`Delegating to '${context.backendName}': ${repr(request)}`);
|
|
562
|
+
const session = await this._openSession();
|
|
563
|
+
const status = await session.send(request);
|
|
564
|
+
await this._trace(params.llm, { event: "request", text: request, backend: status });
|
|
565
|
+
await params.resultCallback({ status: "delegated", backend: status }, { properties: new FunctionCallResultProperties({ runLlm: this._respondOnDelegate }) });
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Deliver one thing the backend produced to the frontend.
|
|
569
|
+
*
|
|
570
|
+
* An output becomes a message appended to the frontend's conversation, which
|
|
571
|
+
* runs the frontend when the output asks to be spoken. A function call phase
|
|
572
|
+
* is reported as the `ExternalFunctionCall*Frame` for it, which the RTVI
|
|
573
|
+
* observer turns into function-call events. An error becomes a spoken
|
|
574
|
+
* message, so the user hears the work stopped. The appended messages are
|
|
575
|
+
* uninterruptible: one queued while the bot speaks waits behind that speech,
|
|
576
|
+
* and an interruption clears the speech but must leave the message to be
|
|
577
|
+
* recorded.
|
|
578
|
+
*
|
|
579
|
+
* @param frontend The frontend service.
|
|
580
|
+
* @param event What the backend produced.
|
|
581
|
+
*/
|
|
582
|
+
async deliver(frontend, event) {
|
|
583
|
+
if (event instanceof BackendOutput) {
|
|
584
|
+
await this._trace(frontend, {
|
|
585
|
+
event: "output",
|
|
586
|
+
text: event.text,
|
|
587
|
+
spoken: event.prefersSpoken,
|
|
588
|
+
thought: event.isThought,
|
|
589
|
+
});
|
|
590
|
+
logger.debug(`Backend output (${event.prefersSpoken ? "spoken" : "silent"}` +
|
|
591
|
+
`${event.isThought ? ", thought" : ""}): ${repr(event.text)}`);
|
|
592
|
+
const message = this.renderOutput(event);
|
|
593
|
+
if (message !== null) {
|
|
594
|
+
await this._append(frontend, new LLMMessagesAppendFrame({ messages: [message], runLlm: event.prefersSpoken }));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
else if (event instanceof BackendToolCall) {
|
|
598
|
+
await this._trace(frontend, {
|
|
599
|
+
event: "tool_call",
|
|
600
|
+
phase: event.phase,
|
|
601
|
+
name: event.functionName,
|
|
602
|
+
arguments: event.arguments,
|
|
603
|
+
result: event.result,
|
|
604
|
+
});
|
|
605
|
+
await frontend.pushFrame(event.toFrame());
|
|
606
|
+
const message = this.renderToolCall(event);
|
|
607
|
+
if (message !== null) {
|
|
608
|
+
await this._append(frontend, new LLMMessagesAppendFrame({ messages: [message], runLlm: false }));
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
else if (event instanceof BackendError) {
|
|
612
|
+
await this._trace(frontend, { event: "error", error: event.error });
|
|
613
|
+
logger.warning(`Backend error: ${event.error}`);
|
|
614
|
+
await this._append(frontend, new LLMMessagesAppendFrame({
|
|
615
|
+
messages: [
|
|
616
|
+
{
|
|
617
|
+
role: "developer",
|
|
618
|
+
content: `${BACKEND_MESSAGE_PREFIX}The work could not be completed.\n\n${RELAY_NOTE}`,
|
|
619
|
+
},
|
|
620
|
+
],
|
|
621
|
+
runLlm: true,
|
|
622
|
+
}));
|
|
623
|
+
}
|
|
624
|
+
else if (event instanceof BackendIdle) {
|
|
625
|
+
await this._trace(frontend, { event: "idle" });
|
|
626
|
+
logger.debug("Backend is idle");
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/** Send one exchange with the backend to the client, when tracing is on. */
|
|
630
|
+
async _trace(frontend, data) {
|
|
631
|
+
if (this._clientTrace) {
|
|
632
|
+
await frontend.pushFrame(new RTVIServerMessageFrame({ data: { type: "llm-with-backend", ...data } }));
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/** Queue a message into the frontend's conversation, past any interruption on the way. */
|
|
636
|
+
async _append(frontend, frame) {
|
|
637
|
+
frame.interruptible = false;
|
|
638
|
+
await frontend.queueFrame(frame);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Render a backend output as the message the frontend's conversation takes in.
|
|
642
|
+
*
|
|
643
|
+
* Override for another wording, role, or to leave some outputs out by
|
|
644
|
+
* returning null. The default marks the message so the frontend can tell it
|
|
645
|
+
* from the user's and its own, and so the transcript request strategy can
|
|
646
|
+
* leave it out of what it sends the backend: a spoken output as `Backend:`
|
|
647
|
+
* with `RELAY_NOTE` after it, a silent one as `Backend (note):`, a reasoning
|
|
648
|
+
* summary as `Backend (thinking):`.
|
|
649
|
+
*
|
|
650
|
+
* @param output The output.
|
|
651
|
+
* @returns The message, or null to deliver nothing for this output.
|
|
652
|
+
*/
|
|
653
|
+
renderOutput(output) {
|
|
654
|
+
if (!output.text) {
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
if (output.isThought) {
|
|
658
|
+
return { role: "developer", content: `${BACKEND_THOUGHT_PREFIX}${output.text}` };
|
|
659
|
+
}
|
|
660
|
+
if (!output.prefersSpoken) {
|
|
661
|
+
return { role: "developer", content: `${BACKEND_NOTE_PREFIX}${output.text}` };
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
role: "developer",
|
|
665
|
+
content: `${BACKEND_MESSAGE_PREFIX}${output.text}\n\n${RELAY_NOTE}`,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Render a phase of a backend function call as a message the frontend's
|
|
670
|
+
* conversation takes in, silently.
|
|
671
|
+
*
|
|
672
|
+
* The default records each call as it starts, with its arguments, so the
|
|
673
|
+
* frontend can say what the backend is doing if asked; the other phases are
|
|
674
|
+
* not recorded. Override to record more, less, or nothing.
|
|
675
|
+
*
|
|
676
|
+
* @param call The phase of the call.
|
|
677
|
+
* @returns The message, or null to record nothing for this phase.
|
|
678
|
+
*/
|
|
679
|
+
renderToolCall(call) {
|
|
680
|
+
if (call.phase !== "in_progress") {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
const args = Object.entries(call.arguments ?? {})
|
|
684
|
+
.map(([k, v]) => `${k}=${repr(v)}`)
|
|
685
|
+
.join(", ");
|
|
686
|
+
return {
|
|
687
|
+
role: "developer",
|
|
688
|
+
content: `${BACKEND_WORKING_PREFIX}${call.functionName}(${args})`,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* A conversational LLM, kept fast and light, with a backend that does the
|
|
694
|
+
* work it hands off.
|
|
695
|
+
*
|
|
696
|
+
* **Alpha.** The API is unstable: names, arguments, the strategies and the job
|
|
697
|
+
* contract are likely to change between releases.
|
|
698
|
+
*
|
|
699
|
+
* Put it where the LLM goes in a pipeline. It is a `Pipeline`, not an
|
|
700
|
+
* `LLMService`: functions, settings updates, event handlers and a
|
|
701
|
+
* `FlowManager` go on `frontend`. It wraps the frontend service, installs the
|
|
702
|
+
* connector's tools on it as built-in tools, appends the connector's guidance
|
|
703
|
+
* to the frontend's system instruction, adds a local backend worker to the
|
|
704
|
+
* pipeline worker so the app never wires it up, and holds the session with
|
|
705
|
+
* the backend for the pipeline's life. A built-in tool is sent on every
|
|
706
|
+
* inference beside whatever tools the frontend has, and never enters the
|
|
707
|
+
* context's tool set, so a tool change announced to the model never mentions
|
|
708
|
+
* it. The tools are the backend's; the frontend has `delegate` and no more,
|
|
709
|
+
* though a tool it must keep, in its context or configured on the service,
|
|
710
|
+
* stays.
|
|
711
|
+
*
|
|
712
|
+
* The guidance is the connector's pairing instruction (how to work with a
|
|
713
|
+
* backend: when and how to delegate, and what to do with the backend's
|
|
714
|
+
* messages) with the strategy's (how the request is worded) in place. The
|
|
715
|
+
* frontend's own system instruction is the place to say what the backend is
|
|
716
|
+
* for, in plain words, as the backend's own instruction does: no tool names,
|
|
717
|
+
* nothing to update when a tool changes.
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* const llm = new LLMWithBackend({
|
|
721
|
+
* frontend: new OpenAILLMService({ ... }),
|
|
722
|
+
* backend: new BackendLLMWorker({
|
|
723
|
+
* llm: new AnthropicLLMService({ ... }),
|
|
724
|
+
* context: new LLMContext({ tools: [getWeather] }),
|
|
725
|
+
* }),
|
|
726
|
+
* });
|
|
727
|
+
* const pipeline = new Pipeline([transport.input(), stt, userAgg, llm, tts, transport.output(), assistantAgg]);
|
|
728
|
+
*/
|
|
729
|
+
export class LLMWithBackend extends Pipeline {
|
|
730
|
+
_frontend;
|
|
731
|
+
_backend;
|
|
732
|
+
_connector;
|
|
733
|
+
_sessionTask = null;
|
|
734
|
+
/**
|
|
735
|
+
* Initialize the service.
|
|
736
|
+
*
|
|
737
|
+
* @throws Error If the frontend service declines the role, or the
|
|
738
|
+
* connector's request strategy reads the frontend's context and the
|
|
739
|
+
* frontend is speech-to-speech.
|
|
740
|
+
*/
|
|
741
|
+
constructor(options) {
|
|
742
|
+
const { frontend, backend } = options;
|
|
743
|
+
logger.warning("LLMWithBackend is alpha: its API is likely to change between releases.");
|
|
744
|
+
const objection = frontend.llmWithBackendRoleObjection("frontend");
|
|
745
|
+
if (objection) {
|
|
746
|
+
throw new Error(objection);
|
|
747
|
+
}
|
|
748
|
+
const connector = options.connector || new BackendConnector();
|
|
749
|
+
connector.bind(new ConnectorContext({
|
|
750
|
+
backendName: typeof backend === "string" ? backend : backend.name,
|
|
751
|
+
frontendIsRealtime: frontend.serviceMetadataFrame().isRealtimeService,
|
|
752
|
+
}));
|
|
753
|
+
const instruction = connector.frontendInstruction;
|
|
754
|
+
if (instruction) {
|
|
755
|
+
frontend.appendSystemInstruction(instruction);
|
|
756
|
+
}
|
|
757
|
+
for (const tool of connector.tools) {
|
|
758
|
+
frontend.registerFunction(tool.name, tool.handler, {
|
|
759
|
+
cancelOnInterruption: false,
|
|
760
|
+
});
|
|
761
|
+
frontend.getLlmAdapter().builtinTools[tool.name] = tool;
|
|
762
|
+
}
|
|
763
|
+
super([frontend]);
|
|
764
|
+
this._frontend = frontend;
|
|
765
|
+
this._backend = backend;
|
|
766
|
+
this._connector = connector;
|
|
767
|
+
}
|
|
768
|
+
/** The frontend LLM service. */
|
|
769
|
+
get frontend() {
|
|
770
|
+
return this._frontend;
|
|
771
|
+
}
|
|
772
|
+
/** The backend worker, or its name. */
|
|
773
|
+
get backend() {
|
|
774
|
+
return this._backend;
|
|
775
|
+
}
|
|
776
|
+
/** The connector joining the two layers. */
|
|
777
|
+
get connector() {
|
|
778
|
+
return this._connector;
|
|
779
|
+
}
|
|
780
|
+
/** The `delegate` tool installed on the frontend. */
|
|
781
|
+
get tool() {
|
|
782
|
+
return this._connector.tool;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Register a local backend worker as a child of the pipeline worker, set up
|
|
786
|
+
* the frontend, and open the session.
|
|
787
|
+
*/
|
|
788
|
+
async setup(setup) {
|
|
789
|
+
// Before setting the frontend up: it gets the StartFrame as soon as its
|
|
790
|
+
// own setup is done, and can delegate from then on.
|
|
791
|
+
if (this._backend instanceof BackendLLMWorker) {
|
|
792
|
+
await setup.pipelineWorker.addWorkers(this._backend);
|
|
793
|
+
}
|
|
794
|
+
await super.setup(setup);
|
|
795
|
+
this._sessionTask = this.createTask(() => this._connector.runSession(this.pipelineWorker, this._frontend), `${this}::backend_session`);
|
|
796
|
+
}
|
|
797
|
+
/** Close the session with the backend, then clean up the frontend. */
|
|
798
|
+
async cleanup() {
|
|
799
|
+
if (this._sessionTask !== null) {
|
|
800
|
+
await this.cancelTask(this._sessionTask);
|
|
801
|
+
this._sessionTask = null;
|
|
802
|
+
}
|
|
803
|
+
await super.cleanup();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
//# sourceMappingURL=llm_with_backend.js.map
|