1use crate::coding::guidance as coding_guidance;
25use crate::engine::{
26 answer_links_notation, language_aware_answer_for, language_aware_intent_for, normalize_prompt,
27 response_link_for_intent, stable_id, ExecutionRecipe, SelectedRule, SymbolicAnswer,
28};
29use crate::event_log::{build_evidence_links, EventLog};
30use crate::intent_formalization::{
31 record_intent_formalization, recover_write_program_rule, rewrite_bare_program_coreference_rule,
32 select_rule_for_intent, IntentFormalizationCache, IntentFormalizationCacheEntry,
33};
34use crate::language::{detect as detect_language, Language};
35use crate::probability::{ProbabilityDecisionPolicy, ProbabilityStore};
36use crate::rule_synthesis::try_construct_unknown_rule;
37use crate::seed;
38use crate::solver_diagnostics::append_diagnostic_trace;
39use crate::solver_formalization::{record_formalization, record_formalization_selection};
40use crate::solver_handler_oracle::try_unsupported_write_program;
41use crate::solver_handlers::{finalize_simple, try_agent_workspace_task, try_program_blueprint};
42use crate::solver_helpers::{
43 confidence_for, is_agent_opt_in, is_agent_request, is_cache_flush_request,
44 is_destructive_action, is_forget_request, is_inappropriate_content, is_unbounded_autonomy,
45 is_unbounded_loop, record_candidates, record_decomposition, record_validation,
46 requires_external_lookup,
47};
48use crate::solver_synthesis::try_synthesize_from_sub_results;
49use crate::solver_unknown_reasoning::{answer_unknown_prompt, UnknownReasoningConfig};
50use crate::translation::{
51 formalize_prompt_candidates, select_formalization_candidate_with_policy, FormalizationDecision,
52 FormalizationSelectionConfig,
53};
54
55#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
60pub enum ExecutionSurface {
61 #[default]
62 RustLibrary,
63 Cli,
64 HttpServer,
65 Browser,
66 Telegram,
67 DockerMicroservice,
68}
69
70impl ExecutionSurface {
71 #[must_use]
72 pub const fn slug(self) -> &'static str {
73 match self {
74 Self::RustLibrary => "rust_library",
75 Self::Cli => "cli",
76 Self::HttpServer => "http_server",
77 Self::Browser => "browser",
78 Self::Telegram => "telegram",
79 Self::DockerMicroservice => "docker_microservice",
80 }
81 }
82
83 pub(crate) fn from_env_value(raw: &str) -> Option<Self> {
84 match raw.trim().to_ascii_lowercase().as_str() {
85 "rust" | "rust_library" | "library" | "lib" => Some(Self::RustLibrary),
86 "cli" | "terminal" | "shell" => Some(Self::Cli),
87 "http" | "http_server" | "server" | "api" => Some(Self::HttpServer),
88 "browser" | "web" | "wasm" | "demo" => Some(Self::Browser),
89 "telegram" | "telegram_bot" | "bot" => Some(Self::Telegram),
90 "docker" | "docker_microservice" | "container" => Some(Self::DockerMicroservice),
91 _ => None,
92 }
93 }
94}
95
96#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
118pub enum BlueprintComposition {
119 #[default]
121 Composed,
122 Documented,
124}
125
126impl BlueprintComposition {
127 #[must_use]
129 pub const fn slug(self) -> &'static str {
130 match self {
131 Self::Composed => "composed",
132 Self::Documented => "documented",
133 }
134 }
135
136 #[must_use]
140 pub fn from_value(raw: &str) -> Option<Self> {
141 match raw.trim().to_ascii_lowercase().as_str() {
142 "composed" | "compose" | "projection" | "project" | "decomposed" => {
143 Some(Self::Composed)
144 }
145 "documented" | "document" | "full" | "verbatim" | "curated" => Some(Self::Documented),
146 _ => None,
147 }
148 }
149}
150
151#[allow(clippy::struct_excessive_bools)]
158#[derive(Debug, Clone, Copy, PartialEq)]
159pub struct SolverConfig {
160 pub guess_probability: f32,
167 pub follow_up_probability: f32,
175 pub context_sensitivity: f32,
177 pub questioning_rigor: f32,
179 pub temperature: f32,
181 pub max_decomposition_depth: u8,
183 pub recursion_mode: crate::meta_construction::RecursionMode,
188 pub selection_mode: crate::selection::SelectionMode,
194 pub skill_mode: crate::skill_ledger::SkillMode,
200 pub agent_mode: bool,
202 pub diagnostic_mode: bool,
204 pub offline: bool,
206 pub cache_ttl_seconds: u64,
208 pub definition_fusion_by_default: bool,
211 pub associative_project_promotion: bool,
215 pub execution_surface: ExecutionSurface,
217 pub blueprint_composition: BlueprintComposition,
220 pub probability_policy: ProbabilityDecisionPolicy,
226 pub forced_response_language: Option<&'static str>,
237 pub compute_budget: u32,
246}
247
248impl Default for SolverConfig {
249 fn default() -> Self {
250 Self {
251 guess_probability: 0.8,
252 follow_up_probability: 0.75,
253 context_sensitivity: 0.6,
254 questioning_rigor: 0.4,
255 temperature: 0.7,
256 max_decomposition_depth: 4,
257 recursion_mode: crate::meta_construction::RecursionMode::default(),
258 selection_mode: crate::selection::SelectionMode::default(),
259 skill_mode: crate::skill_ledger::SkillMode::default(),
260 agent_mode: false,
261 diagnostic_mode: false,
262 offline: false,
263 cache_ttl_seconds: 60 * 60 * 24 * 60,
264 definition_fusion_by_default: false,
265 associative_project_promotion: true,
266 execution_surface: ExecutionSurface::default(),
267 blueprint_composition: BlueprintComposition::default(),
268 probability_policy: ProbabilityDecisionPolicy::default(),
269 forced_response_language: None,
270 compute_budget: 512,
271 }
272 }
273}
274
275impl SolverConfig {
276 #[must_use]
281 pub fn from_env() -> Self {
282 crate::solver_helpers::config_from_env()
283 }
284}
285
286#[derive(Debug, Clone, Copy, PartialEq, Eq)]
290pub enum ConversationRole {
291 User,
292 Assistant,
293}
294
295impl ConversationRole {
296 #[must_use]
298 pub const fn slug(self) -> &'static str {
299 match self {
300 Self::User => "user",
301 Self::Assistant => "assistant",
302 }
303 }
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
312pub struct ConversationTurn {
313 pub role: ConversationRole,
314 pub content: String,
315}
316
317impl ConversationTurn {
318 #[must_use]
320 pub fn user(content: impl Into<String>) -> Self {
321 Self {
322 role: ConversationRole::User,
323 content: content.into(),
324 }
325 }
326
327 #[must_use]
329 pub fn assistant(content: impl Into<String>) -> Self {
330 Self {
331 role: ConversationRole::Assistant,
332 content: content.into(),
333 }
334 }
335}
336
337#[derive(Debug, Clone, Copy)]
339pub struct UniversalSolver {
340 pub config: SolverConfig,
341}
342
343impl Default for UniversalSolver {
344 fn default() -> Self {
345 Self {
346 config: SolverConfig::from_env(),
347 }
348 }
349}
350
351impl UniversalSolver {
352 #[must_use]
354 pub const fn new(config: SolverConfig) -> Self {
355 Self { config }
356 }
357
358 #[must_use]
363 pub fn solve(&self, prompt: &str) -> SymbolicAnswer {
364 self.solve_with_history(prompt, &[])
365 }
366
367 #[must_use]
372 pub fn solve_with_history(&self, prompt: &str, history: &[ConversationTurn]) -> SymbolicAnswer {
373 self.solve_with_history_and_probability_store(prompt, history, &ProbabilityStore::new())
374 }
375
376 #[must_use]
377 pub fn solve_with_probability_store(
378 &self,
379 prompt: &str,
380 probability_store: &ProbabilityStore,
381 ) -> SymbolicAnswer {
382 self.solve_with_history_and_probability_store(prompt, &[], probability_store)
383 }
384
385 #[must_use]
386 pub fn solve_with_history_and_probability_store(
387 &self,
388 prompt: &str,
389 history: &[ConversationTurn],
390 probability_store: &ProbabilityStore,
391 ) -> SymbolicAnswer {
392 let mut intent_cache = IntentFormalizationCache::new();
393 self.solve_with_history_probability_store_and_intent_cache(
394 prompt,
395 history,
396 probability_store,
397 &mut intent_cache,
398 )
399 }
400
401 pub(crate) fn solve_with_history_probability_store_and_intent_cache(
402 &self,
403 prompt: &str,
404 history: &[ConversationTurn],
405 probability_store: &ProbabilityStore,
406 intent_cache: &mut IntentFormalizationCache,
407 ) -> SymbolicAnswer {
408 let mut log = EventLog::new();
409
410 let _forced_language_guard = crate::language::set_forced_language(
415 self.config
416 .forced_response_language
417 .and_then(crate::language::from_slug),
418 );
419
420 for turn in history {
421 let kind: &'static str = match turn.role {
422 ConversationRole::User => "prior_turn:user",
423 ConversationRole::Assistant => "prior_turn:assistant",
424 };
425 log.append(kind, turn.content.clone());
426 }
427
428 log.append("impulse", prompt.to_owned());
429
430 let language = detect_language(prompt);
431 log.append("language", language.slug().to_owned());
432 probability_store.replay_into_event_log(&mut log, self.config.offline);
433
434 let intent_entry = if let Some(formalization) = intent_cache.get(prompt).cloned() {
435 IntentFormalizationCacheEntry {
436 formalization,
437 cache_hit: true,
438 }
439 } else {
440 let formalization_candidates = formalize_prompt_candidates(prompt, language.slug());
441 let formalization_selection = select_formalization_candidate_with_policy(
442 &formalization_candidates,
443 FormalizationSelectionConfig {
444 temperature: self.config.temperature,
445 guess_probability: self.config.guess_probability,
446 questioning_rigor: self.config.questioning_rigor,
447 },
448 prompt,
449 probability_store,
450 self.config.offline,
451 self.config.probability_policy,
452 );
453 record_formalization_selection(&mut log, &formalization_selection);
454 if let FormalizationDecision::Clarify { question, .. } =
455 &formalization_selection.decision
456 {
457 return finalize_simple(
458 prompt,
459 &mut log,
460 "clarify_interpretation",
461 "response:clarify_interpretation",
462 question,
463 0.5,
464 );
465 }
466 if let Some(candidate) = formalization_selection.selected_candidate() {
467 record_formalization(&mut log, candidate);
468 }
469 intent_cache.formalize_or_insert(
470 prompt,
471 language.slug(),
472 formalization_selection.selected_candidate(),
473 )
474 };
475 record_intent_formalization(&mut log, &intent_entry);
476 let intent_formalization = intent_entry.formalization;
477
478 if let Some(answer) = crate::requirement_contradiction::detect_and_report(
485 prompt,
486 language,
487 history,
488 self.config.temperature,
489 &mut log,
490 ) {
491 return answer;
492 }
493
494 crate::meta_core::record_meta_core(
501 &mut log,
502 &intent_formalization,
503 self.config.max_decomposition_depth,
504 self.config.recursion_mode,
505 self.config.selection_mode,
506 self.config.skill_mode,
507 );
508
509 log.append("search:local", prompt.to_owned());
510
511 let sub_impulses =
512 record_decomposition(&mut log, prompt, self.config.max_decomposition_depth);
513 let sub_results =
514 self.solve_sub_impulses(&mut log, &sub_impulses, probability_store, intent_cache);
515
516 let selected_rule = select_rule_for_intent(&intent_formalization);
517 let rule = try_construct_unknown_rule(selected_rule, prompt, history, &mut log);
518 let rule =
519 if let Some(rewrite) = rewrite_bare_program_coreference_rule(&rule, prompt, history) {
520 log.append("write_program_coreference_rewrite", rewrite.trace);
521 rewrite.rule
522 } else {
523 rule
524 };
525
526 let rule = if matches!(rule, SelectedRule::UnsupportedWriteProgram { .. }) {
532 let recovery = recover_write_program_rule(rule, prompt, history);
533 if let Some(trace) = recovery.trace {
534 log.append("write_program_context_recovery", trace);
535 }
536 if let Some(plan) = recovery.plan {
537 log.append("write_program_plan", plan);
538 }
539 recovery.rule
540 } else {
541 rule
542 };
543
544 if !matches!(rule, SelectedRule::WriteProgram(_)) {
550 let language_hint = match &rule {
551 SelectedRule::UnsupportedWriteProgram { language, .. } => language.as_deref(),
552 _ => None,
553 };
554 let normalized_for_blueprint = normalize_prompt(prompt);
555 if let Some(answer) = try_program_blueprint(
556 prompt,
557 &normalized_for_blueprint,
558 language_hint,
559 self.config.blueprint_composition,
560 &mut log,
561 ) {
562 return answer;
563 }
564 }
565
566 if let SelectedRule::UnsupportedWriteProgram { task, language } = &rule {
578 if let Some(answer) = try_unsupported_write_program(
579 prompt,
580 task.as_deref(),
581 language.as_deref(),
582 self.config.blueprint_composition,
583 &mut log,
584 ) {
585 return answer;
586 }
587 }
588
589 if let Some(answer) = try_synthesize_from_sub_results(
590 prompt,
591 &mut log,
592 &sub_results,
593 probability_store,
594 self.config,
595 ) {
596 return answer;
597 }
598
599 let is_concrete_write_program = matches!(rule, SelectedRule::WriteProgram(_));
605 if !is_concrete_write_program {
606 if let Some(answer) = crate::meta_method_dispatch::try_dispatch(
607 self,
608 prompt,
609 &intent_formalization,
610 history,
611 &mut log,
612 ) {
613 return answer;
614 }
615 }
616
617 if let Some(answer) = self.handle_policy(prompt, &mut log, language) {
618 return answer;
619 }
620
621 if matches!(rule, SelectedRule::Unknown) {
622 let intent = language_aware_intent_for(&rule, language);
623 record_candidates(&mut log, prompt, &intent);
624 if let Some(choice) = record_validation(&mut log, prompt) {
625 let response_link = response_link_for_intent(&rule, &intent);
626 return finalize_simple(
627 prompt,
628 &mut log,
629 &intent,
630 &response_link,
631 &choice.answer,
632 1.0,
633 );
634 }
635 if let Some(answer) =
639 crate::solver_terminal::try_terminal_command(prompt, language, &mut log)
640 {
641 return answer;
642 }
643 if let Some(answer) =
649 crate::solver_search::try_budget_search(prompt, &mut log, self.config)
650 {
651 return answer;
652 }
653 if requires_external_lookup(prompt) {
654 self.record_external_search(&mut log, prompt);
655 }
656 return answer_unknown_prompt(
657 prompt,
658 language,
659 &mut log,
660 UnknownReasoningConfig {
661 questioning_rigor: self.config.questioning_rigor,
662 offline: self.config.offline,
663 },
664 );
665 }
666
667 let intent = language_aware_intent_for(&rule, language);
668 log.append("intent", intent.clone());
669
670 if let SelectedRule::WriteProgram(spec) = &rule {
671 log.append(
672 "execution_status",
673 spec.language.execution.status.label().to_owned(),
674 );
675 log.append(
676 "execution_environment",
677 spec.language.execution.environment.to_owned(),
678 );
679 log.append("program_parameter:language", spec.language.slug.to_owned());
680 log.append("program_parameter:task", spec.task.slug.to_owned());
681 log.append("program_parameters", spec.parameter_summary());
682 log.append("legacy_intent", spec.legacy_intent());
683 }
684
685 record_candidates(&mut log, prompt, &intent);
686
687 let validation_choice = record_validation(&mut log, prompt);
688 if validation_choice.is_none() && log.first_of("validation").is_none() {
689 log.append(
690 "validation",
691 "accepted_without_extra_constraints".to_owned(),
692 );
693 }
694 let prior = coding_guidance::history_has_prior_code(history);
695 let base_answer = match (&validation_choice, &rule) {
696 (Some(choice), SelectedRule::Unknown) => choice.answer.clone(),
697 _ => language_aware_answer_for(&rule, language, prompt, prior),
698 };
699
700 let response_link = response_link_for_intent(&rule, &intent);
701 log.append("response", response_link.clone());
702
703 log.append("trace:simplification", "smallest_sufficient".to_owned());
704 let trace_id = log.append("trace", intent.clone());
705
706 let evidence_links = build_evidence_links(prompt, &log, &response_link);
707 let links_notation = answer_links_notation(prompt, &intent, &base_answer, &log, &trace_id);
708 let thinking_steps = log.thinking_steps_for_answer(&base_answer);
709 let answer =
710 append_diagnostic_trace(self.config.diagnostic_mode, base_answer, &links_notation);
711
712 let execution_recipe = match &rule {
713 SelectedRule::WriteProgram(spec) => Some(Box::new(ExecutionRecipe {
714 language: spec.language.code_fence.to_owned(),
715 source: crate::code_editing::apply_inline_hello_world_source_replacement(
716 prompt,
717 spec.template.code,
718 *spec,
719 ),
720 path: spec.language.save_as.to_owned(),
721 commands: spec
722 .language
723 .execution
724 .check_command
725 .into_iter()
726 .chain(std::iter::once(spec.language.execution.run_command))
727 .map(str::to_owned)
728 .collect(),
729 })),
730 _ => None,
731 };
732
733 SymbolicAnswer {
734 intent,
735 answer,
736 confidence: confidence_for(&rule, validation_choice.as_ref()),
737 evidence_links,
738 thinking_steps,
739 links_notation,
740 execution_recipe,
741 }
742 }
743
744 fn handle_policy(
745 &self,
746 prompt: &str,
747 log: &mut EventLog,
748 language: Language,
749 ) -> Option<SymbolicAnswer> {
750 let normalized = prompt.to_lowercase();
751
752 if is_inappropriate_content(&normalized) {
753 log.append("policy:inappropriate_content", prompt.to_owned());
754 let lang_slug = language.slug();
755 let fallback = "That message contains inappropriate content. Please keep the conversation respectful.";
756 let body = seed::response_for("inappropriate_content", lang_slug)
757 .unwrap_or_else(|| String::from(fallback));
758 return Some(Self::finalize_policy(
759 prompt,
760 log,
761 "inappropriate_content",
762 language,
763 &body,
764 ));
765 }
766
767 if is_unbounded_autonomy(&normalized) && !is_agent_opt_in(&normalized) {
768 log.append("policy:chat_bounded_autonomy", prompt.to_owned());
769 return Some(Self::finalize_policy(
770 prompt,
771 log,
772 "bounded_autonomy",
773 language,
774 concat!(
775 "I can only run a bounded chat reply per message. To take repeated, ",
776 "open-ended actions I need an explicit opt-in to agent mode, and agent ",
777 "mode runs in an isolated sandbox so the host stays safe."
778 ),
779 ));
780 }
781
782 if is_forget_request(&normalized) {
783 log.append("policy:add_only_history", prompt.to_owned());
784 return Some(Self::finalize_policy(
785 prompt,
786 log,
787 "add_only_history",
788 language,
789 concat!(
790 "The link network is append-only. To retract a fact, send the explicit ",
791 "retraction protocol; it will append a superseding event without erasing ",
792 "history."
793 ),
794 ));
795 }
796
797 if is_cache_flush_request(&normalized) {
798 log.append(
799 "policy:cache_flush_requires_confirmation",
800 prompt.to_owned(),
801 );
802 return Some(Self::finalize_policy(
803 prompt,
804 log,
805 "cache_flush_requires_confirmation",
806 language,
807 "Flushing the source cache is an auditable action. Confirm explicitly.",
808 ));
809 }
810
811 if is_agent_request(&normalized) && is_destructive_action(&normalized) {
812 log.append("agent_mode:opted_in", prompt.to_owned());
813 log.append(
814 "policy:destructive_action_requires_confirmation",
815 prompt.to_owned(),
816 );
817 return Some(Self::finalize_policy(
818 prompt,
819 log,
820 "destructive_action_requires_confirmation",
821 language,
822 concat!(
823 "Destructive agent actions require an explicit human confirmation. ",
824 "The action will run inside an isolated sandbox once confirmed."
825 ),
826 ));
827 }
828
829 if is_agent_request(&normalized) && is_unbounded_loop(&normalized) {
830 log.append("agent_mode:opted_in", prompt.to_owned());
831 log.append("policy:agent_time_budget", prompt.to_owned());
832 return Some(Self::finalize_policy(
833 prompt,
834 log,
835 "agent_time_budget",
836 language,
837 concat!(
838 "Agent execution is bounded by a documented time budget; unbounded ",
839 "loops are refused. Re-send a bounded version inside an isolated sandbox."
840 ),
841 ));
842 }
843
844 if is_agent_request(&normalized) {
845 if self.config.execution_surface != ExecutionSurface::HttpServer {
851 if let Some(answer) = try_agent_workspace_task(prompt, &normalized, log) {
852 return Some(answer);
853 }
854 }
855 log.append("agent_mode:opted_in", prompt.to_owned());
856 log.append("agent_mode:active", prompt.to_owned());
857 log.append("action_log", prompt.to_owned());
858 return Some(Self::finalize_policy(
859 prompt,
860 log,
861 "agent_action",
862 language,
863 concat!(
864 "Agent mode is opted in for this message. The action will run inside ",
865 "an isolated sandbox (docker, webvm or sandbox-equivalent) and every ",
866 "step will be appended to the action log."
867 ),
868 ));
869 }
870
871 None
872 }
873
874 fn finalize_policy(
875 prompt: &str,
876 log: &mut EventLog,
877 intent_slug: &str,
878 _language: Language,
879 body: &str,
880 ) -> SymbolicAnswer {
881 let intent = format!("policy_{intent_slug}");
882 let response_link = format!("response:policy:{intent_slug}");
883 finalize_simple(prompt, log, &intent, &response_link, body, 0.5)
884 }
885
886 fn record_external_search(&self, log: &mut EventLog, prompt: &str) {
887 if self.config.offline {
888 log.append("search:external", "skipped:offline".to_owned());
889 return;
890 }
891 log.append("search:external", prompt.to_owned());
892 let source_id = stable_id("source", prompt);
893 let fetched_at = "1970-01-01T00:00:00Z";
894 let sha256 = stable_id("sha256", prompt);
895 log.append(
896 "source:http",
897 format!("https://example.org/{source_id} fetched_at={fetched_at} sha256={sha256}"),
898 );
899 log.append("cache_hit", source_id);
900 }
901}
902
903#[must_use]
907pub fn solve(prompt: &str) -> SymbolicAnswer {
908 UniversalSolver::default().solve(prompt)
909}
910
911#[must_use]
913pub fn solve_with_history(prompt: &str, history: &[ConversationTurn]) -> SymbolicAnswer {
914 UniversalSolver::default().solve_with_history(prompt, history)
915}