1use std::collections::BTreeMap;
2use std::num::NonZeroUsize;
3
4use deserr::Deserr;
5use roaring::RoaringBitmap;
6use serde::{Deserialize, Serialize};
7use utoipa::ToSchema;
8
9use super::composite::SubEmbedderOptions;
10use super::hf::OverridePooling;
11use super::{ollama, openai, DistributionShift, EmbedderOptions};
12use crate::prompt::{default_max_bytes, PromptData};
13use crate::update::Setting;
14use crate::vector::EmbeddingConfig;
15use crate::UserError;
16
17#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Deserr, ToSchema)]
18#[serde(deny_unknown_fields, rename_all = "camelCase")]
19#[deserr(rename_all = camelCase, deny_unknown_fields)]
20pub struct EmbeddingSettings {
21 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
22 #[deserr(default)]
23 #[schema(value_type = Option<EmbedderSource>)]
24 pub source: Setting<EmbedderSource>,
36 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
37 #[deserr(default)]
38 #[schema(value_type = Option<String>)]
39 pub model: Setting<String>,
58 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
59 #[deserr(default)]
60 #[schema(value_type = Option<String>)]
61 pub revision: Setting<String>,
78 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
79 #[deserr(default)]
80 #[schema(value_type = Option<OverridePooling>)]
81 pub pooling: Setting<OverridePooling>,
99 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
100 #[deserr(default)]
101 #[schema(value_type = Option<String>)]
102 pub api_key: Setting<String>,
121 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
122 #[deserr(default)]
123 #[schema(value_type = Option<String>)]
124 pub dimensions: Setting<usize>,
144 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
145 #[deserr(default)]
146 #[schema(value_type = Option<bool>)]
147 pub binary_quantized: Setting<bool>,
170 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
171 #[deserr(default)]
172 #[schema(value_type = Option<bool>)]
173 pub document_template: Setting<String>,
186 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
187 #[deserr(default)]
188 #[schema(value_type = Option<usize>)]
189 pub document_template_max_bytes: Setting<usize>,
204 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
205 #[deserr(default)]
206 #[schema(value_type = Option<String>)]
207 pub url: Setting<String>,
222 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
223 #[deserr(default)]
224 #[schema(value_type = Option<serde_json::Value>)]
225 pub request: Setting<serde_json::Value>,
239 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
240 #[deserr(default)]
241 #[schema(value_type = Option<serde_json::Value>)]
242 pub response: Setting<serde_json::Value>,
256 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
257 #[deserr(default)]
258 #[schema(value_type = Option<BTreeMap<String, String>>)]
259 pub headers: Setting<BTreeMap<String, String>>,
269
270 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
271 #[deserr(default)]
272 #[schema(value_type = Option<SubEmbeddingSettings>)]
273 pub search_embedder: Setting<SubEmbeddingSettings>,
274
275 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
276 #[deserr(default)]
277 #[schema(value_type = Option<SubEmbeddingSettings>)]
278 pub indexing_embedder: Setting<SubEmbeddingSettings>,
279
280 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
281 #[deserr(default)]
282 #[schema(value_type = Option<DistributionShift>)]
283 pub distribution: Setting<DistributionShift>,
293}
294
295#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq, Deserr, ToSchema)]
296#[serde(deny_unknown_fields, rename_all = "camelCase")]
297#[deserr(rename_all = camelCase, deny_unknown_fields)]
298pub struct SubEmbeddingSettings {
299 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
300 #[deserr(default)]
301 #[schema(value_type = Option<EmbedderSource>)]
302 pub source: Setting<EmbedderSource>,
314 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
315 #[deserr(default)]
316 #[schema(value_type = Option<String>)]
317 pub model: Setting<String>,
336 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
337 #[deserr(default)]
338 #[schema(value_type = Option<String>)]
339 pub revision: Setting<String>,
356 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
357 #[deserr(default)]
358 #[schema(value_type = Option<OverridePooling>)]
359 pub pooling: Setting<OverridePooling>,
377 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
378 #[deserr(default)]
379 #[schema(value_type = Option<String>)]
380 pub api_key: Setting<String>,
399 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
400 #[deserr(default)]
401 #[schema(value_type = Option<String>)]
402 pub dimensions: Setting<usize>,
422 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
423 #[deserr(default)]
424 #[schema(value_type = Option<bool>)]
425 pub document_template: Setting<String>,
438 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
439 #[deserr(default)]
440 #[schema(value_type = Option<usize>)]
441 pub document_template_max_bytes: Setting<usize>,
456 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
457 #[deserr(default)]
458 #[schema(value_type = Option<String>)]
459 pub url: Setting<String>,
474 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
475 #[deserr(default)]
476 #[schema(value_type = Option<serde_json::Value>)]
477 pub request: Setting<serde_json::Value>,
491 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
492 #[deserr(default)]
493 #[schema(value_type = Option<serde_json::Value>)]
494 pub response: Setting<serde_json::Value>,
508 #[serde(default, skip_serializing_if = "Setting::is_not_set")]
509 #[deserr(default)]
510 #[schema(value_type = Option<BTreeMap<String, String>>)]
511 pub headers: Setting<BTreeMap<String, String>>,
521
522 #[serde(default, skip_serializing)]
525 #[deserr(default)]
526 #[schema(ignore)]
527 pub distribution: Setting<DistributionShift>,
528
529 #[serde(default, skip_serializing)]
530 #[deserr(default)]
531 #[schema(ignore)]
532 pub binary_quantized: Setting<bool>,
533
534 #[serde(default, skip_serializing)]
535 #[deserr(default)]
536 #[schema(ignore)]
537 pub search_embedder: Setting<serde_json::Value>,
538
539 #[serde(default, skip_serializing)]
540 #[deserr(default)]
541 #[schema(ignore)]
542 pub indexing_embedder: Setting<serde_json::Value>,
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
547pub enum ReindexAction {
548 RegeneratePrompts,
551 FullReindex,
554}
555
556pub enum SettingsDiff {
557 Remove,
558 Reindex { action: ReindexAction, updated_settings: EmbeddingSettings, quantize: bool },
559 UpdateWithoutReindex { updated_settings: EmbeddingSettings, quantize: bool },
560}
561
562#[derive(Default, Debug)]
563pub struct EmbedderAction {
564 pub was_quantized: bool,
565 pub is_being_quantized: bool,
566 pub write_back: Option<WriteBackToDocuments>,
567 pub reindex: Option<ReindexAction>,
568}
569
570impl EmbedderAction {
571 pub fn is_being_quantized(&self) -> bool {
572 self.is_being_quantized
573 }
574
575 pub fn write_back(&self) -> Option<&WriteBackToDocuments> {
576 self.write_back.as_ref()
577 }
578
579 pub fn reindex(&self) -> Option<&ReindexAction> {
580 self.reindex.as_ref()
581 }
582
583 pub fn with_is_being_quantized(mut self, quantize: bool) -> Self {
584 self.is_being_quantized = quantize;
585 self
586 }
587
588 pub fn with_write_back(write_back: WriteBackToDocuments, was_quantized: bool) -> Self {
589 Self {
590 was_quantized,
591 is_being_quantized: false,
592 write_back: Some(write_back),
593 reindex: None,
594 }
595 }
596
597 pub fn with_reindex(reindex: ReindexAction, was_quantized: bool) -> Self {
598 Self { was_quantized, is_being_quantized: false, write_back: None, reindex: Some(reindex) }
599 }
600}
601
602#[derive(Debug)]
603pub struct WriteBackToDocuments {
604 pub embedder_id: u8,
605 pub user_provided: RoaringBitmap,
606}
607
608impl SettingsDiff {
609 pub fn from_settings(
610 embedder_name: &str,
611 old: EmbeddingSettings,
612 new: Setting<EmbeddingSettings>,
613 ) -> Result<Self, UserError> {
614 let ret = match new {
615 Setting::Set(new) => {
616 let EmbeddingSettings {
617 mut source,
618 mut model,
619 mut revision,
620 mut pooling,
621 mut api_key,
622 mut dimensions,
623 mut document_template,
624 mut url,
625 mut request,
626 mut response,
627 mut search_embedder,
628 mut indexing_embedder,
629 mut distribution,
630 mut headers,
631 mut document_template_max_bytes,
632 binary_quantized: mut binary_quantize,
633 } = old;
634
635 let EmbeddingSettings {
636 source: new_source,
637 model: new_model,
638 revision: new_revision,
639 pooling: new_pooling,
640 api_key: new_api_key,
641 dimensions: new_dimensions,
642 document_template: new_document_template,
643 url: new_url,
644 request: new_request,
645 response: new_response,
646 search_embedder: new_search_embedder,
647 indexing_embedder: new_indexing_embedder,
648 distribution: new_distribution,
649 headers: new_headers,
650 document_template_max_bytes: new_document_template_max_bytes,
651 binary_quantized: new_binary_quantize,
652 } = new;
653
654 if matches!(binary_quantize, Setting::Set(true))
655 && matches!(new_binary_quantize, Setting::Set(false))
656 {
657 return Err(UserError::InvalidDisableBinaryQuantization {
658 embedder_name: embedder_name.to_string(),
659 });
660 }
661
662 let mut reindex_action = None;
663
664 Self::apply_and_diff(
665 &mut reindex_action,
666 &mut source,
667 &mut model,
668 &mut revision,
669 &mut pooling,
670 &mut api_key,
671 &mut dimensions,
672 &mut document_template,
673 &mut document_template_max_bytes,
674 &mut url,
675 &mut request,
676 &mut response,
677 &mut headers,
678 new_source,
679 new_model,
680 new_revision,
681 new_pooling,
682 new_api_key,
683 new_dimensions,
684 new_document_template,
685 new_document_template_max_bytes,
686 new_url,
687 new_request,
688 new_response,
689 new_headers,
690 );
691
692 let binary_quantize_changed = binary_quantize.apply(new_binary_quantize);
693
694 search_embedder.apply(new_search_embedder);
696 indexing_embedder = Self::from_sub_settings(
697 indexing_embedder,
698 new_indexing_embedder,
699 &mut reindex_action,
700 )?;
701
702 distribution.apply(new_distribution);
703
704 let updated_settings = EmbeddingSettings {
705 source,
706 model,
707 revision,
708 pooling,
709 api_key,
710 dimensions,
711 document_template,
712 url,
713 request,
714 response,
715 search_embedder,
716 indexing_embedder,
717 distribution,
718 headers,
719 document_template_max_bytes,
720 binary_quantized: binary_quantize,
721 };
722
723 match reindex_action {
724 Some(action) => Self::Reindex {
725 action,
726 updated_settings,
727 quantize: binary_quantize_changed,
728 },
729 None => Self::UpdateWithoutReindex {
730 updated_settings,
731 quantize: binary_quantize_changed,
732 },
733 }
734 }
735 Setting::Reset => Self::Remove,
736 Setting::NotSet => {
737 Self::UpdateWithoutReindex { updated_settings: old, quantize: false }
738 }
739 };
740 Ok(ret)
741 }
742
743 fn from_sub_settings(
744 sub_embedder: Setting<SubEmbeddingSettings>,
745 new_sub_embedder: Setting<SubEmbeddingSettings>,
746 reindex_action: &mut Option<ReindexAction>,
747 ) -> Result<Setting<SubEmbeddingSettings>, UserError> {
748 let ret = match new_sub_embedder {
749 Setting::Set(new_sub_embedder) => {
750 let Setting::Set(SubEmbeddingSettings {
751 mut source,
752 mut model,
753 mut revision,
754 mut pooling,
755 mut api_key,
756 mut dimensions,
757 mut document_template,
758 mut document_template_max_bytes,
759 mut url,
760 mut request,
761 mut response,
762 mut headers,
763 mut distribution,
765 mut binary_quantized,
766 mut search_embedder,
767 mut indexing_embedder,
768 }) = sub_embedder
769 else {
770 return Ok(Setting::Set(new_sub_embedder));
773 };
774
775 let SubEmbeddingSettings {
776 source: new_source,
777 model: new_model,
778 revision: new_revision,
779 pooling: new_pooling,
780 api_key: new_api_key,
781 dimensions: new_dimensions,
782 document_template: new_document_template,
783 document_template_max_bytes: new_document_template_max_bytes,
784 url: new_url,
785 request: new_request,
786 response: new_response,
787 headers: new_headers,
788 distribution: new_distribution,
789 binary_quantized: new_binary_quantized,
790 search_embedder: new_search_embedder,
791 indexing_embedder: new_indexing_embedder,
792 } = new_sub_embedder;
793
794 Self::apply_and_diff(
795 reindex_action,
796 &mut source,
797 &mut model,
798 &mut revision,
799 &mut pooling,
800 &mut api_key,
801 &mut dimensions,
802 &mut document_template,
803 &mut document_template_max_bytes,
804 &mut url,
805 &mut request,
806 &mut response,
807 &mut headers,
808 new_source,
809 new_model,
810 new_revision,
811 new_pooling,
812 new_api_key,
813 new_dimensions,
814 new_document_template,
815 new_document_template_max_bytes,
816 new_url,
817 new_request,
818 new_response,
819 new_headers,
820 );
821
822 distribution.apply(new_distribution);
824 binary_quantized.apply(new_binary_quantized);
825 search_embedder.apply(new_search_embedder);
826 indexing_embedder.apply(new_indexing_embedder);
827
828 let updated_settings = SubEmbeddingSettings {
829 source,
830 model,
831 revision,
832 pooling,
833 api_key,
834 dimensions,
835 document_template,
836 url,
837 request,
838 response,
839 headers,
840 document_template_max_bytes,
841 distribution,
842 binary_quantized,
843 search_embedder,
844 indexing_embedder,
845 };
846 Setting::Set(updated_settings)
847 }
848 Setting::Reset | Setting::NotSet => sub_embedder,
850 };
851 Ok(ret)
852 }
853
854 #[allow(clippy::too_many_arguments)]
855 fn apply_and_diff(
856 reindex_action: &mut Option<ReindexAction>,
857 source: &mut Setting<EmbedderSource>,
858 model: &mut Setting<String>,
859 revision: &mut Setting<String>,
860 pooling: &mut Setting<OverridePooling>,
861 api_key: &mut Setting<String>,
862 dimensions: &mut Setting<usize>,
863 document_template: &mut Setting<String>,
864 document_template_max_bytes: &mut Setting<usize>,
865 url: &mut Setting<String>,
866 request: &mut Setting<serde_json::Value>,
867 response: &mut Setting<serde_json::Value>,
868 headers: &mut Setting<BTreeMap<String, String>>,
869 new_source: Setting<EmbedderSource>,
870 new_model: Setting<String>,
871 new_revision: Setting<String>,
872 new_pooling: Setting<OverridePooling>,
873 new_api_key: Setting<String>,
874 new_dimensions: Setting<usize>,
875 new_document_template: Setting<String>,
876 new_document_template_max_bytes: Setting<usize>,
877 new_url: Setting<String>,
878 new_request: Setting<serde_json::Value>,
879 new_response: Setting<serde_json::Value>,
880 new_headers: Setting<BTreeMap<String, String>>,
881 ) {
882 if source.apply(new_source) {
884 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
885 apply_default_for_source(
887 &*source,
888 model,
889 revision,
890 pooling,
891 dimensions,
892 url,
893 request,
894 response,
895 document_template,
896 document_template_max_bytes,
897 headers,
898 &mut Setting::NotSet,
900 &mut Setting::NotSet,
901 )
902 }
903 if model.apply(new_model) {
904 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
905 }
906 if revision.apply(new_revision) {
907 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
908 }
909 if pooling.apply(new_pooling) {
910 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
911 }
912 if dimensions.apply(new_dimensions) {
913 match *source {
914 Setting::Set(EmbedderSource::OpenAi) | Setting::Reset => {
916 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
917 }
918 _ => {}
921 }
922 }
923 if url.apply(new_url) {
924 match *source {
925 Setting::Set(EmbedderSource::OpenAi) | Setting::Reset => {}
927 _ => {
928 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
929 }
930 }
931 }
932 if request.apply(new_request) {
933 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
934 }
935 if response.apply(new_response) {
936 ReindexAction::push_action(reindex_action, ReindexAction::FullReindex);
937 }
938 if document_template.apply(new_document_template) {
939 ReindexAction::push_action(reindex_action, ReindexAction::RegeneratePrompts);
940 }
941
942 if document_template_max_bytes.apply(new_document_template_max_bytes) {
943 let previous_document_template_max_bytes =
944 document_template_max_bytes.set().unwrap_or(default_max_bytes().get());
945 let new_document_template_max_bytes =
946 new_document_template_max_bytes.set().unwrap_or(default_max_bytes().get());
947
948 if new_document_template_max_bytes > previous_document_template_max_bytes {
952 ReindexAction::push_action(reindex_action, ReindexAction::RegeneratePrompts)
953 }
954 }
955
956 api_key.apply(new_api_key);
957 headers.apply(new_headers);
958 }
959}
960
961impl ReindexAction {
962 fn push_action(this: &mut Option<Self>, other: Self) {
963 *this = match (*this, other) {
964 (_, ReindexAction::FullReindex) => Some(ReindexAction::FullReindex),
965 (Some(ReindexAction::FullReindex), _) => Some(ReindexAction::FullReindex),
966 (_, ReindexAction::RegeneratePrompts) => Some(ReindexAction::RegeneratePrompts),
967 }
968 }
969}
970
971#[allow(clippy::too_many_arguments)] fn apply_default_for_source(
973 source: &Setting<EmbedderSource>,
974 model: &mut Setting<String>,
975 revision: &mut Setting<String>,
976 pooling: &mut Setting<OverridePooling>,
977 dimensions: &mut Setting<usize>,
978 url: &mut Setting<String>,
979 request: &mut Setting<serde_json::Value>,
980 response: &mut Setting<serde_json::Value>,
981 document_template: &mut Setting<String>,
982 document_template_max_bytes: &mut Setting<usize>,
983 headers: &mut Setting<BTreeMap<String, String>>,
984 search_embedder: &mut Setting<SubEmbeddingSettings>,
985 indexing_embedder: &mut Setting<SubEmbeddingSettings>,
986) {
987 match source {
988 Setting::Set(EmbedderSource::HuggingFace) => {
989 *model = Setting::Reset;
990 *revision = Setting::Reset;
991 *pooling = Setting::Reset;
992 *dimensions = Setting::NotSet;
993 *url = Setting::NotSet;
994 *request = Setting::NotSet;
995 *response = Setting::NotSet;
996 *headers = Setting::NotSet;
997 *search_embedder = Setting::NotSet;
998 *indexing_embedder = Setting::NotSet;
999 }
1000 Setting::Set(EmbedderSource::Ollama) => {
1001 *model = Setting::Reset;
1002 *revision = Setting::NotSet;
1003 *pooling = Setting::NotSet;
1004 *dimensions = Setting::Reset;
1005 *url = Setting::NotSet;
1006 *request = Setting::NotSet;
1007 *response = Setting::NotSet;
1008 *headers = Setting::NotSet;
1009 *search_embedder = Setting::NotSet;
1010 *indexing_embedder = Setting::NotSet;
1011 }
1012 Setting::Set(EmbedderSource::OpenAi) | Setting::Reset => {
1013 *model = Setting::Reset;
1014 *revision = Setting::NotSet;
1015 *pooling = Setting::NotSet;
1016 *dimensions = Setting::NotSet;
1017 *url = Setting::Reset;
1018 *request = Setting::NotSet;
1019 *response = Setting::NotSet;
1020 *headers = Setting::NotSet;
1021 *search_embedder = Setting::NotSet;
1022 *indexing_embedder = Setting::NotSet;
1023 }
1024 Setting::Set(EmbedderSource::Rest) => {
1025 *model = Setting::NotSet;
1026 *revision = Setting::NotSet;
1027 *pooling = Setting::NotSet;
1028 *dimensions = Setting::Reset;
1029 *url = Setting::Reset;
1030 *request = Setting::Reset;
1031 *response = Setting::Reset;
1032 *headers = Setting::Reset;
1033 *search_embedder = Setting::NotSet;
1034 *indexing_embedder = Setting::NotSet;
1035 }
1036 Setting::Set(EmbedderSource::UserProvided) => {
1037 *model = Setting::NotSet;
1038 *revision = Setting::NotSet;
1039 *pooling = Setting::NotSet;
1040 *dimensions = Setting::Reset;
1041 *url = Setting::NotSet;
1042 *request = Setting::NotSet;
1043 *response = Setting::NotSet;
1044 *document_template = Setting::NotSet;
1045 *document_template_max_bytes = Setting::NotSet;
1046 *headers = Setting::NotSet;
1047 *search_embedder = Setting::NotSet;
1048 *indexing_embedder = Setting::NotSet;
1049 }
1050 Setting::Set(EmbedderSource::Composite) => {
1051 *model = Setting::NotSet;
1052 *revision = Setting::NotSet;
1053 *pooling = Setting::NotSet;
1054 *dimensions = Setting::NotSet;
1055 *url = Setting::NotSet;
1056 *request = Setting::NotSet;
1057 *response = Setting::NotSet;
1058 *document_template = Setting::NotSet;
1059 *document_template_max_bytes = Setting::NotSet;
1060 *headers = Setting::NotSet;
1061 *search_embedder = Setting::Reset;
1062 *indexing_embedder = Setting::Reset;
1063 }
1064 Setting::NotSet => {}
1065 }
1066}
1067
1068pub(crate) enum FieldStatus {
1069 Mandatory,
1070 Allowed,
1071 Disallowed,
1072}
1073
1074#[derive(Debug, Clone, Copy)]
1075pub enum NestingContext {
1076 NotNested,
1077 Search,
1078 Indexing,
1079}
1080
1081impl NestingContext {
1082 pub fn embedder_name_with_context(&self, embedder_name: &str) -> String {
1083 match self {
1084 NestingContext::NotNested => embedder_name.to_string(),
1085 NestingContext::Search => format!("{embedder_name}.searchEmbedder"),
1086 NestingContext::Indexing => format!("{embedder_name}.indexingEmbedder",),
1087 }
1088 }
1089
1090 pub fn in_context(&self) -> &'static str {
1091 match self {
1092 NestingContext::NotNested => "",
1093 NestingContext::Search => " for the search embedder",
1094 NestingContext::Indexing => " for the indexing embedder",
1095 }
1096 }
1097
1098 pub fn nesting_embedders(&self) -> &'static str {
1099 match self {
1100 NestingContext::NotNested => "",
1101 NestingContext::Search => {
1102 "\n - note: nesting embedders in `searchEmbedder` is not allowed"
1103 }
1104 NestingContext::Indexing => {
1105 "\n - note: nesting embedders in `indexingEmbedder` is not allowed"
1106 }
1107 }
1108 }
1109}
1110
1111#[derive(Debug, Clone, Copy, enum_iterator::Sequence)]
1112pub enum MetaEmbeddingSetting {
1113 Source,
1114 Model,
1115 Revision,
1116 Pooling,
1117 ApiKey,
1118 Dimensions,
1119 DocumentTemplate,
1120 DocumentTemplateMaxBytes,
1121 Url,
1122 Request,
1123 Response,
1124 Headers,
1125 SearchEmbedder,
1126 IndexingEmbedder,
1127 Distribution,
1128 BinaryQuantized,
1129}
1130
1131impl MetaEmbeddingSetting {
1132 pub(crate) fn name(&self) -> &'static str {
1133 use MetaEmbeddingSetting::*;
1134 match self {
1135 Source => "source",
1136 Model => "model",
1137 Revision => "revision",
1138 Pooling => "pooling",
1139 ApiKey => "apiKey",
1140 Dimensions => "dimensions",
1141 DocumentTemplate => "documentTemplate",
1142 DocumentTemplateMaxBytes => "documentTemplateMaxBytes",
1143 Url => "url",
1144 Request => "request",
1145 Response => "response",
1146 Headers => "headers",
1147 SearchEmbedder => "searchEmbedder",
1148 IndexingEmbedder => "indexingEmbedder",
1149 Distribution => "distribution",
1150 BinaryQuantized => "binaryQuantized",
1151 }
1152 }
1153}
1154
1155impl EmbeddingSettings {
1156 #[allow(clippy::too_many_arguments)]
1157 pub(crate) fn check_settings(
1158 embedder_name: &str,
1159 source: EmbedderSource,
1160 context: NestingContext,
1161 model: &Setting<String>,
1162 revision: &Setting<String>,
1163 pooling: &Setting<OverridePooling>,
1164 dimensions: &Setting<usize>,
1165 api_key: &Setting<String>,
1166 url: &Setting<String>,
1167 request: &Setting<serde_json::Value>,
1168 response: &Setting<serde_json::Value>,
1169 document_template: &Setting<String>,
1170 document_template_max_bytes: &Setting<usize>,
1171 headers: &Setting<BTreeMap<String, String>>,
1172 search_embedder: &Setting<SubEmbeddingSettings>,
1173 indexing_embedder: &Setting<SubEmbeddingSettings>,
1174 binary_quantized: &Setting<bool>,
1175 distribution: &Setting<DistributionShift>,
1176 ) -> Result<(), UserError> {
1177 Self::check_setting(embedder_name, source, MetaEmbeddingSetting::Model, context, model)?;
1178 Self::check_setting(
1179 embedder_name,
1180 source,
1181 MetaEmbeddingSetting::Revision,
1182 context,
1183 revision,
1184 )?;
1185 Self::check_setting(
1186 embedder_name,
1187 source,
1188 MetaEmbeddingSetting::Pooling,
1189 context,
1190 pooling,
1191 )?;
1192 Self::check_setting(
1193 embedder_name,
1194 source,
1195 MetaEmbeddingSetting::Dimensions,
1196 context,
1197 dimensions,
1198 )?;
1199 Self::check_setting(embedder_name, source, MetaEmbeddingSetting::ApiKey, context, api_key)?;
1200 Self::check_setting(embedder_name, source, MetaEmbeddingSetting::Url, context, url)?;
1201 Self::check_setting(
1202 embedder_name,
1203 source,
1204 MetaEmbeddingSetting::Request,
1205 context,
1206 request,
1207 )?;
1208 Self::check_setting(
1209 embedder_name,
1210 source,
1211 MetaEmbeddingSetting::Response,
1212 context,
1213 response,
1214 )?;
1215 Self::check_setting(
1216 embedder_name,
1217 source,
1218 MetaEmbeddingSetting::DocumentTemplate,
1219 context,
1220 document_template,
1221 )?;
1222 Self::check_setting(
1223 embedder_name,
1224 source,
1225 MetaEmbeddingSetting::DocumentTemplateMaxBytes,
1226 context,
1227 document_template_max_bytes,
1228 )?;
1229 Self::check_setting(
1230 embedder_name,
1231 source,
1232 MetaEmbeddingSetting::Headers,
1233 context,
1234 headers,
1235 )?;
1236 Self::check_setting(
1237 embedder_name,
1238 source,
1239 MetaEmbeddingSetting::SearchEmbedder,
1240 context,
1241 search_embedder,
1242 )?;
1243 Self::check_setting(
1244 embedder_name,
1245 source,
1246 MetaEmbeddingSetting::IndexingEmbedder,
1247 context,
1248 indexing_embedder,
1249 )?;
1250 Self::check_setting(
1251 embedder_name,
1252 source,
1253 MetaEmbeddingSetting::BinaryQuantized,
1254 context,
1255 binary_quantized,
1256 )?;
1257 Self::check_setting(
1258 embedder_name,
1259 source,
1260 MetaEmbeddingSetting::Distribution,
1261 context,
1262 distribution,
1263 )
1264 }
1265
1266 pub(crate) fn allowed_sources_for_field(
1267 field: MetaEmbeddingSetting,
1268 context: NestingContext,
1269 ) -> Vec<EmbedderSource> {
1270 enum_iterator::all()
1271 .filter(|source| {
1272 !matches!(Self::field_status(*source, field, context), FieldStatus::Disallowed)
1273 })
1274 .collect()
1275 }
1276
1277 pub(crate) fn allowed_fields_for_source(
1278 source: EmbedderSource,
1279 context: NestingContext,
1280 ) -> Vec<&'static str> {
1281 enum_iterator::all()
1282 .filter(|field| {
1283 !matches!(Self::field_status(source, *field, context), FieldStatus::Disallowed)
1284 })
1285 .map(|field| field.name())
1286 .collect()
1287 }
1288
1289 fn check_setting<T>(
1290 embedder_name: &str,
1291 source: EmbedderSource,
1292 field: MetaEmbeddingSetting,
1293 context: NestingContext,
1294 setting: &Setting<T>,
1295 ) -> Result<(), UserError> {
1296 match (Self::field_status(source, field, context), setting) {
1297 (FieldStatus::Mandatory, Setting::Set(_))
1298 | (FieldStatus::Allowed, _)
1299 | (FieldStatus::Disallowed, Setting::NotSet) => Ok(()),
1300 (FieldStatus::Disallowed, _) => Err(UserError::InvalidFieldForSource {
1301 embedder_name: context.embedder_name_with_context(embedder_name),
1302 source_: source,
1303 context,
1304 field,
1305 }),
1306 (FieldStatus::Mandatory, _) => Err(UserError::MissingFieldForSource {
1307 field: field.name(),
1308 source_: source,
1309 embedder_name: embedder_name.to_owned(),
1310 }),
1311 }
1312 }
1313
1314 pub(crate) fn field_status(
1315 source: EmbedderSource,
1316 field: MetaEmbeddingSetting,
1317 context: NestingContext,
1318 ) -> FieldStatus {
1319 use EmbedderSource::*;
1320 use MetaEmbeddingSetting::*;
1321 use NestingContext::*;
1322 match (source, field, context) {
1323 (_, Distribution | BinaryQuantized, NotNested) => FieldStatus::Allowed,
1324 (_, Distribution | BinaryQuantized, _) => FieldStatus::Disallowed,
1325 (_, DocumentTemplate | DocumentTemplateMaxBytes, Search) => FieldStatus::Disallowed,
1326 (
1327 OpenAi,
1328 Source
1329 | Model
1330 | ApiKey
1331 | DocumentTemplate
1332 | DocumentTemplateMaxBytes
1333 | Dimensions
1334 | Url,
1335 _,
1336 ) => FieldStatus::Allowed,
1337 (
1338 OpenAi,
1339 Revision | Pooling | Request | Response | Headers | SearchEmbedder
1340 | IndexingEmbedder,
1341 _,
1342 ) => FieldStatus::Disallowed,
1343 (
1344 HuggingFace,
1345 Source | Model | Revision | Pooling | DocumentTemplate | DocumentTemplateMaxBytes,
1346 _,
1347 ) => FieldStatus::Allowed,
1348 (
1349 HuggingFace,
1350 ApiKey | Dimensions | Url | Request | Response | Headers | SearchEmbedder
1351 | IndexingEmbedder,
1352 _,
1353 ) => FieldStatus::Disallowed,
1354 (Ollama, Model, _) => FieldStatus::Mandatory,
1355 (
1356 Ollama,
1357 Source | DocumentTemplate | DocumentTemplateMaxBytes | Url | ApiKey | Dimensions,
1358 _,
1359 ) => FieldStatus::Allowed,
1360 (
1361 Ollama,
1362 Revision | Pooling | Request | Response | Headers | SearchEmbedder
1363 | IndexingEmbedder,
1364 _,
1365 ) => FieldStatus::Disallowed,
1366 (UserProvided, Dimensions, _) => FieldStatus::Mandatory,
1367 (UserProvided, Source, _) => FieldStatus::Allowed,
1368 (
1369 UserProvided,
1370 Model
1371 | Revision
1372 | Pooling
1373 | ApiKey
1374 | DocumentTemplate
1375 | DocumentTemplateMaxBytes
1376 | Url
1377 | Request
1378 | Response
1379 | Headers
1380 | SearchEmbedder
1381 | IndexingEmbedder,
1382 _,
1383 ) => FieldStatus::Disallowed,
1384 (Rest, Url | Request | Response, _) => FieldStatus::Mandatory,
1385 (
1386 Rest,
1387 Source
1388 | ApiKey
1389 | Dimensions
1390 | DocumentTemplate
1391 | DocumentTemplateMaxBytes
1392 | Headers,
1393 _,
1394 ) => FieldStatus::Allowed,
1395 (Rest, Model | Revision | Pooling | SearchEmbedder | IndexingEmbedder, _) => {
1396 FieldStatus::Disallowed
1397 }
1398 (Composite, SearchEmbedder | IndexingEmbedder, _) => FieldStatus::Mandatory,
1399 (Composite, Source, _) => FieldStatus::Allowed,
1400 (
1401 Composite,
1402 Model
1403 | Revision
1404 | Pooling
1405 | ApiKey
1406 | Dimensions
1407 | DocumentTemplate
1408 | DocumentTemplateMaxBytes
1409 | Url
1410 | Request
1411 | Response
1412 | Headers,
1413 _,
1414 ) => FieldStatus::Disallowed,
1415 }
1416 }
1417
1418 pub(crate) fn apply_default_source(setting: &mut Setting<EmbeddingSettings>) {
1419 if let Setting::Set(EmbeddingSettings {
1420 source: source @ (Setting::NotSet | Setting::Reset),
1421 ..
1422 }) = setting
1423 {
1424 *source = Setting::Set(EmbedderSource::default())
1425 }
1426 }
1427
1428 pub(crate) fn apply_default_openai_model(setting: &mut Setting<EmbeddingSettings>) {
1429 if let Setting::Set(EmbeddingSettings {
1430 source: Setting::Set(EmbedderSource::OpenAi),
1431 model: model @ (Setting::NotSet | Setting::Reset),
1432 ..
1433 }) = setting
1434 {
1435 *model = Setting::Set(openai::EmbeddingModel::default().name().to_owned())
1436 }
1437 }
1438
1439 pub(crate) fn check_nested_source(
1440 embedder_name: &str,
1441 source: EmbedderSource,
1442 context: NestingContext,
1443 ) -> Result<(), UserError> {
1444 match (context, source) {
1445 (NestingContext::NotNested, _) => Ok(()),
1446 (
1447 NestingContext::Search | NestingContext::Indexing,
1448 EmbedderSource::Composite | EmbedderSource::UserProvided,
1449 ) => Err(UserError::InvalidSourceForNested {
1450 embedder_name: context.embedder_name_with_context(embedder_name),
1451 source_: source,
1452 }),
1453 (
1454 NestingContext::Search | NestingContext::Indexing,
1455 EmbedderSource::OpenAi
1456 | EmbedderSource::HuggingFace
1457 | EmbedderSource::Ollama
1458 | EmbedderSource::Rest,
1459 ) => Ok(()),
1460 }
1461 }
1462}
1463
1464#[derive(
1465 Debug,
1466 Clone,
1467 Copy,
1468 Default,
1469 Serialize,
1470 Deserialize,
1471 PartialEq,
1472 Eq,
1473 Deserr,
1474 ToSchema,
1475 enum_iterator::Sequence,
1476)]
1477#[serde(deny_unknown_fields, rename_all = "camelCase")]
1478#[deserr(rename_all = camelCase, deny_unknown_fields)]
1479pub enum EmbedderSource {
1480 #[default]
1481 OpenAi,
1482 HuggingFace,
1483 Ollama,
1484 UserProvided,
1485 Rest,
1486 Composite,
1487}
1488
1489impl std::fmt::Display for EmbedderSource {
1490 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1491 let s = match self {
1492 EmbedderSource::OpenAi => "openAi",
1493 EmbedderSource::HuggingFace => "huggingFace",
1494 EmbedderSource::UserProvided => "userProvided",
1495 EmbedderSource::Ollama => "ollama",
1496 EmbedderSource::Rest => "rest",
1497 EmbedderSource::Composite => "composite",
1498 };
1499 f.write_str(s)
1500 }
1501}
1502
1503impl EmbeddingSettings {
1504 fn from_hugging_face(
1505 super::hf::EmbedderOptions {
1506 model,
1507 revision,
1508 distribution,
1509 pooling,
1510 }: super::hf::EmbedderOptions,
1511 document_template: Setting<String>,
1512 document_template_max_bytes: Setting<usize>,
1513 quantized: Option<bool>,
1514 ) -> Self {
1515 Self {
1516 source: Setting::Set(EmbedderSource::HuggingFace),
1517 model: Setting::Set(model),
1518 revision: Setting::some_or_not_set(revision),
1519 pooling: Setting::Set(pooling),
1520 api_key: Setting::NotSet,
1521 dimensions: Setting::NotSet,
1522 document_template,
1523 document_template_max_bytes,
1524 url: Setting::NotSet,
1525 request: Setting::NotSet,
1526 response: Setting::NotSet,
1527 headers: Setting::NotSet,
1528 search_embedder: Setting::NotSet,
1529 indexing_embedder: Setting::NotSet,
1530 distribution: Setting::some_or_not_set(distribution),
1531 binary_quantized: Setting::some_or_not_set(quantized),
1532 }
1533 }
1534
1535 fn from_openai(
1536 super::openai::EmbedderOptions {
1537 url,
1538 api_key,
1539 embedding_model,
1540 dimensions,
1541 distribution,
1542 }: super::openai::EmbedderOptions,
1543 document_template: Setting<String>,
1544 document_template_max_bytes: Setting<usize>,
1545 quantized: Option<bool>,
1546 ) -> Self {
1547 Self {
1548 source: Setting::Set(EmbedderSource::OpenAi),
1549 model: Setting::Set(embedding_model.name().to_owned()),
1550 revision: Setting::NotSet,
1551 pooling: Setting::NotSet,
1552 api_key: Setting::some_or_not_set(api_key),
1553 dimensions: Setting::some_or_not_set(dimensions),
1554 document_template,
1555 document_template_max_bytes,
1556 url: Setting::some_or_not_set(url),
1557 request: Setting::NotSet,
1558 response: Setting::NotSet,
1559 headers: Setting::NotSet,
1560 search_embedder: Setting::NotSet,
1561 indexing_embedder: Setting::NotSet,
1562 distribution: Setting::some_or_not_set(distribution),
1563 binary_quantized: Setting::some_or_not_set(quantized),
1564 }
1565 }
1566
1567 fn from_ollama(
1568 super::ollama::EmbedderOptions {
1569 embedding_model,
1570 url,
1571 api_key,
1572 distribution,
1573 dimensions,
1574 }: super::ollama::EmbedderOptions,
1575 document_template: Setting<String>,
1576 document_template_max_bytes: Setting<usize>,
1577 quantized: Option<bool>,
1578 ) -> Self {
1579 Self {
1580 source: Setting::Set(EmbedderSource::Ollama),
1581 model: Setting::Set(embedding_model),
1582 revision: Setting::NotSet,
1583 pooling: Setting::NotSet,
1584 api_key: Setting::some_or_not_set(api_key),
1585 dimensions: Setting::some_or_not_set(dimensions),
1586 document_template,
1587 document_template_max_bytes,
1588 url: Setting::some_or_not_set(url),
1589 request: Setting::NotSet,
1590 response: Setting::NotSet,
1591 headers: Setting::NotSet,
1592 search_embedder: Setting::NotSet,
1593 indexing_embedder: Setting::NotSet,
1594 distribution: Setting::some_or_not_set(distribution),
1595 binary_quantized: Setting::some_or_not_set(quantized),
1596 }
1597 }
1598
1599 fn from_user_provided(
1600 super::manual::EmbedderOptions { dimensions, distribution }: super::manual::EmbedderOptions,
1601 quantized: Option<bool>,
1602 ) -> Self {
1603 Self {
1604 source: Setting::Set(EmbedderSource::UserProvided),
1605 model: Setting::NotSet,
1606 revision: Setting::NotSet,
1607 pooling: Setting::NotSet,
1608 api_key: Setting::NotSet,
1609 dimensions: Setting::Set(dimensions),
1610 document_template: Setting::NotSet,
1611 document_template_max_bytes: Setting::NotSet,
1612 url: Setting::NotSet,
1613 request: Setting::NotSet,
1614 response: Setting::NotSet,
1615 headers: Setting::NotSet,
1616 search_embedder: Setting::NotSet,
1617 indexing_embedder: Setting::NotSet,
1618 distribution: Setting::some_or_not_set(distribution),
1619 binary_quantized: Setting::some_or_not_set(quantized),
1620 }
1621 }
1622
1623 fn from_rest(
1624 super::rest::EmbedderOptions {
1625 api_key,
1626 dimensions,
1627 url,
1628 request,
1629 response,
1630 distribution,
1631 headers,
1632 }: super::rest::EmbedderOptions,
1633 document_template: Setting<String>,
1634 document_template_max_bytes: Setting<usize>,
1635 quantized: Option<bool>,
1636 ) -> Self {
1637 Self {
1638 source: Setting::Set(EmbedderSource::Rest),
1639 model: Setting::NotSet,
1640 revision: Setting::NotSet,
1641 pooling: Setting::NotSet,
1642 api_key: Setting::some_or_not_set(api_key),
1643 dimensions: Setting::some_or_not_set(dimensions),
1644 document_template,
1645 document_template_max_bytes,
1646 url: Setting::Set(url),
1647 request: Setting::Set(request),
1648 response: Setting::Set(response),
1649 distribution: Setting::some_or_not_set(distribution),
1650 headers: Setting::Set(headers),
1651 search_embedder: Setting::NotSet,
1652 indexing_embedder: Setting::NotSet,
1653 binary_quantized: Setting::some_or_not_set(quantized),
1654 }
1655 }
1656}
1657
1658impl From<EmbeddingConfig> for EmbeddingSettings {
1659 fn from(value: EmbeddingConfig) -> Self {
1660 let EmbeddingConfig { embedder_options, prompt, quantized } = value;
1661 let document_template_max_bytes =
1662 Setting::Set(prompt.max_bytes.unwrap_or(default_max_bytes()).get());
1663 match embedder_options {
1664 super::EmbedderOptions::HuggingFace(options) => Self::from_hugging_face(
1665 options,
1666 Setting::Set(prompt.template),
1667 document_template_max_bytes,
1668 quantized,
1669 ),
1670 super::EmbedderOptions::OpenAi(options) => Self::from_openai(
1671 options,
1672 Setting::Set(prompt.template),
1673 document_template_max_bytes,
1674 quantized,
1675 ),
1676 super::EmbedderOptions::Ollama(options) => Self::from_ollama(
1677 options,
1678 Setting::Set(prompt.template),
1679 document_template_max_bytes,
1680 quantized,
1681 ),
1682 super::EmbedderOptions::UserProvided(options) => {
1683 Self::from_user_provided(options, quantized)
1684 }
1685 super::EmbedderOptions::Rest(options) => Self::from_rest(
1686 options,
1687 Setting::Set(prompt.template),
1688 document_template_max_bytes,
1689 quantized,
1690 ),
1691 super::EmbedderOptions::Composite(super::composite::EmbedderOptions {
1692 search,
1693 index,
1694 }) => Self {
1695 source: Setting::Set(EmbedderSource::Composite),
1696 model: Setting::NotSet,
1697 revision: Setting::NotSet,
1698 pooling: Setting::NotSet,
1699 api_key: Setting::NotSet,
1700 dimensions: Setting::NotSet,
1701 binary_quantized: Setting::some_or_not_set(quantized),
1702 document_template: Setting::NotSet,
1703 document_template_max_bytes: Setting::NotSet,
1704 url: Setting::NotSet,
1705 request: Setting::NotSet,
1706 response: Setting::NotSet,
1707 headers: Setting::NotSet,
1708 distribution: Setting::some_or_not_set(search.distribution()),
1709 search_embedder: Setting::Set(SubEmbeddingSettings::from_options(
1710 search,
1711 Setting::NotSet,
1712 Setting::NotSet,
1713 )),
1714 indexing_embedder: Setting::Set(SubEmbeddingSettings::from_options(
1715 index,
1716 Setting::Set(prompt.template),
1717 document_template_max_bytes,
1718 )),
1719 },
1720 }
1721 }
1722}
1723
1724impl SubEmbeddingSettings {
1725 fn from_options(
1726 options: SubEmbedderOptions,
1727 document_template: Setting<String>,
1728 document_template_max_bytes: Setting<usize>,
1729 ) -> Self {
1730 let settings = match options {
1731 SubEmbedderOptions::HuggingFace(embedder_options) => {
1732 EmbeddingSettings::from_hugging_face(
1733 embedder_options,
1734 document_template,
1735 document_template_max_bytes,
1736 None,
1737 )
1738 }
1739 SubEmbedderOptions::OpenAi(embedder_options) => EmbeddingSettings::from_openai(
1740 embedder_options,
1741 document_template,
1742 document_template_max_bytes,
1743 None,
1744 ),
1745 SubEmbedderOptions::Ollama(embedder_options) => EmbeddingSettings::from_ollama(
1746 embedder_options,
1747 document_template,
1748 document_template_max_bytes,
1749 None,
1750 ),
1751 SubEmbedderOptions::UserProvided(embedder_options) => {
1752 EmbeddingSettings::from_user_provided(embedder_options, None)
1753 }
1754 SubEmbedderOptions::Rest(embedder_options) => EmbeddingSettings::from_rest(
1755 embedder_options,
1756 document_template,
1757 document_template_max_bytes,
1758 None,
1759 ),
1760 };
1761 settings.into()
1762 }
1763}
1764
1765impl From<EmbeddingSettings> for SubEmbeddingSettings {
1766 fn from(value: EmbeddingSettings) -> Self {
1767 let EmbeddingSettings {
1768 source,
1769 model,
1770 revision,
1771 pooling,
1772 api_key,
1773 dimensions,
1774 document_template,
1775 document_template_max_bytes,
1776 url,
1777 request,
1778 response,
1779 headers,
1780 binary_quantized: _,
1781 search_embedder: _,
1782 indexing_embedder: _,
1783 distribution: _,
1784 } = value;
1785 Self {
1786 source,
1787 model,
1788 revision,
1789 pooling,
1790 api_key,
1791 dimensions,
1792 document_template,
1793 document_template_max_bytes,
1794 url,
1795 request,
1796 response,
1797 headers,
1798 distribution: Setting::NotSet,
1799 binary_quantized: Setting::NotSet,
1800 search_embedder: Setting::NotSet,
1801 indexing_embedder: Setting::NotSet,
1802 }
1803 }
1804}
1805
1806impl From<EmbeddingSettings> for EmbeddingConfig {
1807 fn from(value: EmbeddingSettings) -> Self {
1808 let mut this = Self::default();
1809 let EmbeddingSettings {
1810 source,
1811 model,
1812 revision,
1813 pooling,
1814 api_key,
1815 dimensions,
1816 document_template,
1817 document_template_max_bytes,
1818 url,
1819 request,
1820 response,
1821 distribution,
1822 headers,
1823 binary_quantized,
1824 search_embedder,
1825 mut indexing_embedder,
1826 } = value;
1827
1828 this.quantized = binary_quantized.set();
1829 if let Some((template, document_template_max_bytes)) =
1830 match (document_template, &mut indexing_embedder) {
1831 (Setting::Set(template), _) => Some((template, document_template_max_bytes)),
1832 (
1834 _,
1835 Setting::Set(SubEmbeddingSettings {
1836 document_template: Setting::Set(document_template),
1837 document_template_max_bytes,
1838 ..
1839 }),
1840 ) => Some((std::mem::take(document_template), *document_template_max_bytes)),
1841 _ => None,
1842 }
1843 {
1844 let max_bytes = document_template_max_bytes
1845 .set()
1846 .and_then(NonZeroUsize::new)
1847 .unwrap_or(default_max_bytes());
1848
1849 this.prompt = PromptData { template, max_bytes: Some(max_bytes) }
1850 }
1851
1852 if let Some(source) = source.set() {
1853 this.embedder_options = match source {
1854 EmbedderSource::OpenAi => {
1855 SubEmbedderOptions::openai(model, url, api_key, dimensions, distribution).into()
1856 }
1857 EmbedderSource::Ollama => {
1858 SubEmbedderOptions::ollama(model, url, api_key, dimensions, distribution).into()
1859 }
1860 EmbedderSource::HuggingFace => {
1861 SubEmbedderOptions::hugging_face(model, revision, pooling, distribution).into()
1862 }
1863 EmbedderSource::UserProvided => {
1864 SubEmbedderOptions::user_provided(dimensions.set().unwrap(), distribution)
1865 .into()
1866 }
1867 EmbedderSource::Rest => SubEmbedderOptions::rest(
1868 url.set().unwrap(),
1869 api_key,
1870 request.set().unwrap(),
1871 response.set().unwrap(),
1872 headers,
1873 dimensions,
1874 distribution,
1875 )
1876 .into(),
1877 EmbedderSource::Composite => {
1878 super::EmbedderOptions::Composite(super::composite::EmbedderOptions {
1879 search: SubEmbedderOptions::from_settings(
1881 search_embedder.set().unwrap(),
1882 distribution,
1883 ),
1884 index: SubEmbedderOptions::from_settings(
1885 indexing_embedder.set().unwrap(),
1886 Setting::NotSet,
1887 ),
1888 })
1889 }
1890 };
1891 }
1892
1893 this
1894 }
1895}
1896
1897impl SubEmbedderOptions {
1898 fn from_settings(
1899 settings: SubEmbeddingSettings,
1900 distribution: Setting<DistributionShift>,
1901 ) -> Self {
1902 let SubEmbeddingSettings {
1903 source,
1904 model,
1905 revision,
1906 pooling,
1907 api_key,
1908 dimensions,
1909 document_template: _,
1911 document_template_max_bytes: _,
1912 url,
1913 request,
1914 response,
1915 headers,
1916 distribution: _,
1918 binary_quantized: _,
1919 search_embedder: _,
1920 indexing_embedder: _,
1921 } = settings;
1922
1923 match source.set().unwrap() {
1924 EmbedderSource::OpenAi => Self::openai(model, url, api_key, dimensions, distribution),
1925 EmbedderSource::HuggingFace => {
1926 Self::hugging_face(model, revision, pooling, distribution)
1927 }
1928 EmbedderSource::Ollama => Self::ollama(model, url, api_key, dimensions, distribution),
1929 EmbedderSource::UserProvided => {
1930 Self::user_provided(dimensions.set().unwrap(), distribution)
1931 }
1932 EmbedderSource::Rest => Self::rest(
1933 url.set().unwrap(),
1934 api_key,
1935 request.set().unwrap(),
1936 response.set().unwrap(),
1937 headers,
1938 dimensions,
1939 distribution,
1940 ),
1941 EmbedderSource::Composite => panic!("nested composite embedders"),
1942 }
1943 }
1944
1945 fn openai(
1946 model: Setting<String>,
1947 url: Setting<String>,
1948 api_key: Setting<String>,
1949 dimensions: Setting<usize>,
1950 distribution: Setting<DistributionShift>,
1951 ) -> Self {
1952 let mut options = super::openai::EmbedderOptions::with_default_model(None);
1953 if let Some(model) = model.set() {
1954 if let Some(model) = super::openai::EmbeddingModel::from_name(&model) {
1955 options.embedding_model = model;
1956 }
1957 }
1958 if let Some(url) = url.set() {
1959 options.url = Some(url);
1960 }
1961 if let Some(api_key) = api_key.set() {
1962 options.api_key = Some(api_key);
1963 }
1964 if let Some(dimensions) = dimensions.set() {
1965 options.dimensions = Some(dimensions);
1966 }
1967 options.distribution = distribution.set();
1968 SubEmbedderOptions::OpenAi(options)
1969 }
1970 fn hugging_face(
1971 model: Setting<String>,
1972 revision: Setting<String>,
1973 pooling: Setting<OverridePooling>,
1974 distribution: Setting<DistributionShift>,
1975 ) -> Self {
1976 let mut options = super::hf::EmbedderOptions::default();
1977 if let Some(model) = model.set() {
1978 options.model = model;
1979 options.revision = None;
1985 }
1986 if let Some(revision) = revision.set() {
1987 options.revision = Some(revision);
1988 }
1989 if let Some(pooling) = pooling.set() {
1990 options.pooling = pooling;
1991 }
1992 options.distribution = distribution.set();
1993 SubEmbedderOptions::HuggingFace(options)
1994 }
1995 fn user_provided(dimensions: usize, distribution: Setting<DistributionShift>) -> Self {
1996 Self::UserProvided(super::manual::EmbedderOptions {
1997 dimensions,
1998 distribution: distribution.set(),
1999 })
2000 }
2001 fn rest(
2002 url: String,
2003 api_key: Setting<String>,
2004 request: serde_json::Value,
2005 response: serde_json::Value,
2006 headers: Setting<BTreeMap<String, String>>,
2007 dimensions: Setting<usize>,
2008 distribution: Setting<DistributionShift>,
2009 ) -> Self {
2010 Self::Rest(super::rest::EmbedderOptions {
2011 api_key: api_key.set(),
2012 dimensions: dimensions.set(),
2013 url,
2014 request,
2015 response,
2016 distribution: distribution.set(),
2017 headers: headers.set().unwrap_or_default(),
2018 })
2019 }
2020 fn ollama(
2021 model: Setting<String>,
2022 url: Setting<String>,
2023 api_key: Setting<String>,
2024 dimensions: Setting<usize>,
2025 distribution: Setting<DistributionShift>,
2026 ) -> Self {
2027 let mut options: ollama::EmbedderOptions =
2028 super::ollama::EmbedderOptions::with_default_model(
2029 api_key.set(),
2030 url.set(),
2031 dimensions.set(),
2032 );
2033 if let Some(model) = model.set() {
2034 options.embedding_model = model;
2035 }
2036
2037 options.distribution = distribution.set();
2038 SubEmbedderOptions::Ollama(options)
2039 }
2040}
2041
2042impl From<SubEmbedderOptions> for EmbedderOptions {
2043 fn from(value: SubEmbedderOptions) -> Self {
2044 match value {
2045 SubEmbedderOptions::HuggingFace(embedder_options) => {
2046 Self::HuggingFace(embedder_options)
2047 }
2048 SubEmbedderOptions::OpenAi(embedder_options) => Self::OpenAi(embedder_options),
2049 SubEmbedderOptions::Ollama(embedder_options) => Self::Ollama(embedder_options),
2050 SubEmbedderOptions::UserProvided(embedder_options) => {
2051 Self::UserProvided(embedder_options)
2052 }
2053 SubEmbedderOptions::Rest(embedder_options) => Self::Rest(embedder_options),
2054 }
2055 }
2056}