diff --git a/config/acquia_dev/config_ignore.settings.yml b/config/acquia_dev/config_ignore.settings.yml index 8a1703ea7b..05bc18d297 100644 --- a/config/acquia_dev/config_ignore.settings.yml +++ b/config/acquia_dev/config_ignore.settings.yml @@ -4,7 +4,9 @@ ignored_config_entities: - bos_city_score.settings - bos_core.settings - bos_emergency_alerts.settings + - 'bos_feedback_form:handlers.post_to_zencity.settings.*' - bos_google_cloud.settings + - bos_google_cloud.prompts - bos_mnl.settings - bos_swiftype.settings - 'core.entity_view_display.node.metrolist_development.*' diff --git a/config/acquia_dev/samlauth.authentication.yml b/config/acquia_dev/samlauth.authentication.yml index 463052cebc..14f9d2829e 100644 --- a/config/acquia_dev/samlauth.authentication.yml +++ b/config/acquia_dev/samlauth.authentication.yml @@ -25,10 +25,10 @@ sp_new_certificate: '' sp_private_key: '' metadata_valid_secs: 60 metadata_cache_http: false -idp_entity_id: 'urn:test:sso:boston.gov' -idp_single_sign_on_service: 'https://sso-test.boston.gov/idp/SSO.saml2' -idp_single_log_out_service: 'https://sso-test.boston.gov/idp/startSLO.ping' -idp_change_password_service: 'https://access-test.boston.gov/change-password' +idp_entity_id: 'urn:sso:boston.gov' +idp_single_sign_on_service: 'https://sso.boston.gov/idp/SSO.saml2' +idp_single_log_out_service: 'https://sso.boston.gov/idp/startSLO.ping' +idp_change_password_service: 'https://access.boston.gov/change-password' idp_certs: - 'key:iam_dir_sso_test_' idp_cert_encryption: '' diff --git a/config/acquia_prod/config_ignore.settings.yml b/config/acquia_prod/config_ignore.settings.yml index c8cf50b5dc..b586d5a4b7 100644 --- a/config/acquia_prod/config_ignore.settings.yml +++ b/config/acquia_prod/config_ignore.settings.yml @@ -4,7 +4,9 @@ ignored_config_entities: - bos_city_score.settings - bos_core.settings - bos_emergency_alerts.settings + - 'bos_feedback_form:handlers.post_to_zencity.settings.*' - bos_google_cloud.settings + - bos_google_cloud.prompts - bos_mnl.settings - bos_swiftype.settings - 'core.entity_view_display.node.metrolist_development.*' diff --git a/config/acquia_prod/redirect_domain.domains.yml b/config/acquia_prod/redirect_domain.domains.yml index 38bc5c91ea..d5839630a2 100644 --- a/config/acquia_prod/redirect_domain.domains.yml +++ b/config/acquia_prod/redirect_domain.domains.yml @@ -8,11 +8,11 @@ domain_redirects: 'budget:boston:gov': - sub_path: /* - destination: www.boston.gov/finance/fiscal-year-2024 + destination: www.boston.gov/finance/fiscal-year-2025 'budgets:boston:gov': - sub_path: /* - destination: www.boston.gov/finance/fiscal-year-2024 + destination: www.boston.gov/finance/fiscal-year-2025 'permits:boston:gov': - sub_path: /* diff --git a/config/acquia_stage/config_ignore.settings.yml b/config/acquia_stage/config_ignore.settings.yml index 98043526eb..2bf8e3317c 100644 --- a/config/acquia_stage/config_ignore.settings.yml +++ b/config/acquia_stage/config_ignore.settings.yml @@ -4,7 +4,9 @@ ignored_config_entities: - bos_city_score.settings - bos_core.settings - bos_emergency_alerts.settings + - 'bos_feedback_form:handlers.post_to_zencity.settings.*' - bos_google_cloud.settings + - bos_google_cloud.prompts - bos_mnl.settings - bos_swiftype.settings - 'core.entity_view_display.node.metrolist_development.*' diff --git a/config/acquia_stage/samlauth.authentication.yml b/config/acquia_stage/samlauth.authentication.yml index 25f6cd96ad..33aeb584d0 100644 --- a/config/acquia_stage/samlauth.authentication.yml +++ b/config/acquia_stage/samlauth.authentication.yml @@ -25,10 +25,10 @@ sp_new_certificate: '' sp_private_key: '' metadata_valid_secs: 60 metadata_cache_http: false -idp_entity_id: 'urn:test:sso:boston.gov' -idp_single_sign_on_service: 'https://sso-test.boston.gov/idp/SSO.saml2' -idp_single_log_out_service: 'https://sso-test.boston.gov/idp/startSLO.ping' -idp_change_password_service: 'https://access-test.boston.gov/change-password' +idp_entity_id: 'urn:sso:boston.gov' +idp_single_sign_on_service: 'https://sso.boston.gov/idp/SSO.saml2' +idp_single_log_out_service: 'https://sso.boston.gov/idp/startSLO.ping' +idp_change_password_service: 'https://access.boston.gov/change-password' idp_certs: - 'key:iam_dir_sso_test_' idp_cert_encryption: '' diff --git a/config/default/block.block.bos_theme_feedbackform.yml b/config/default/block.block.bos_theme_feedbackform.yml new file mode 100644 index 0000000000..d4f6b9d01f --- /dev/null +++ b/config/default/block.block.bos_theme_feedbackform.yml @@ -0,0 +1,28 @@ +uuid: 9d6fda0b-6462-4ed5-a6cd-319a833771f1 +langcode: en +status: true +dependencies: + module: + - bos_feedback_form + - system + theme: + - bos_theme +id: bos_theme_feedbackform +theme: bos_theme +region: content +weight: 10 +provider: null +plugin: feedback_form +settings: + id: feedback_form + label: 'Feedback Form' + label_display: '0' + provider: bos_feedback_form + feedback_button_title: 'Provide Your Feedback' + feedback_wrapper_css: ta--c + feedback_button_css: '' +visibility: + request_path: + id: request_path + negate: true + pages: "/buildinghousing/*\r\n/buildinghousing-map\r\n/metrolist*\r\n/metrolist/*\r\n/departments/mayors-office/green-new-deal-dashboard\r\n/admin*\r\n/user*\r\n/departments/registry-birth-death-and-marriage" diff --git a/config/default/block.block.ml_listing_entry.yml b/config/default/block.block.ml_listing_entry.yml index a988b52b40..c2103fda14 100644 --- a/config/default/block.block.ml_listing_entry.yml +++ b/config/default/block.block.ml_listing_entry.yml @@ -12,7 +12,7 @@ dependencies: id: ml_listing_entry theme: bos_theme region: content -weight: 0 +weight: -1 provider: null plugin: webform_block settings: diff --git a/config/default/block.block.website_feedback_form.yml b/config/default/block.block.website_feedback_form.yml index 0f46c8875d..400a2f43cf 100644 --- a/config/default/block.block.website_feedback_form.yml +++ b/config/default/block.block.website_feedback_form.yml @@ -1,6 +1,6 @@ uuid: ff051dac-f44d-4fd4-a7bd-203d54d670e0 langcode: en -status: true +status: false dependencies: config: - webform.webform.website_feedback_form @@ -12,7 +12,7 @@ dependencies: id: website_feedback_form theme: bos_theme region: content -weight: 0 +weight: 1 provider: null plugin: webform_block settings: diff --git a/config/default/field.field.node.program_initiative_profile.field_components.yml b/config/default/field.field.node.program_initiative_profile.field_components.yml index 7bbf8aedc5..4c9100e6b3 100644 --- a/config/default/field.field.node.program_initiative_profile.field_components.yml +++ b/config/default/field.field.node.program_initiative_profile.field_components.yml @@ -7,6 +7,7 @@ dependencies: - node.type.program_initiative_profile - paragraphs.paragraphs_type.3_column_w_image - paragraphs.paragraphs_type.bos311 + - paragraphs.paragraphs_type.bos_node_search - paragraphs.paragraphs_type.bos_signup_emergency_alerts - paragraphs.paragraphs_type.branded_links - paragraphs.paragraphs_type.cabinet @@ -76,8 +77,8 @@ settings: group_of_links_mini_grid: group_of_links_mini_grid hero_image: hero_image iframe: iframe - map: map list: list + map: map news_and_announcements: news_and_announcements newsletter: newsletter photo: photo @@ -89,6 +90,7 @@ settings: commission_summary: commission_summary branded_links: branded_links commission_search: commission_search + bos_node_search: bos_node_search embed_view: embed_view from_library: from_library group_of_links_quick_links: group_of_links_quick_links @@ -104,6 +106,9 @@ settings: bos311: weight: -95 enabled: true + bos_node_search: + weight: 81 + enabled: true bos_signup_emergency_alerts: weight: -53 enabled: true @@ -152,6 +157,18 @@ settings: drawers: weight: -86 enabled: true + election_area_results: + weight: 96 + enabled: false + election_candidate_results: + weight: 97 + enabled: false + election_card: + weight: 98 + enabled: false + election_contest_results: + weight: 99 + enabled: false election_results: weight: -51 enabled: false @@ -254,6 +271,9 @@ settings: quote: weight: -55 enabled: false + roll_call_vote: + weight: 133 + enabled: false seamless_doc: weight: -42 enabled: false diff --git a/config/default/locale.settings.yml b/config/default/locale.settings.yml index 98358ba6ad..a3086eb275 100644 --- a/config/default/locale.settings.yml +++ b/config/default/locale.settings.yml @@ -12,4 +12,4 @@ translation: overwrite_not_customized: true update_interval_days: 0 path: sites/default/files/translations - import_enabled: true + import_enabled: false diff --git a/config/default/webform.webform.metrolist_listing.yml b/config/default/webform.webform.metrolist_listing.yml index 69b16dbf1a..9a2d9565eb 100644 --- a/config/default/webform.webform.metrolist_listing.yml +++ b/config/default/webform.webform.metrolist_listing.yml @@ -1369,7 +1369,7 @@ elements: |- class: - m-v600 style: 'float:right;margin-bottom:1rem;' -css: "#seal {\r\n display:none;\r\n}\r\n\r\n.webform-submission-form .sel-c {\r\n display: block; \r\n}\r\n.webform-submission-form .webform-multiple-table td {\r\n border-bottom: solid 3px;\r\n padding-left: 1.5rem;\r\n}\r\n\r\n.webform-submission-form .form-checkboxes .js-form-type-checkbox {\r\n -webkit-box-align: center;\r\n -ms-flex-align: center;\r\n align-items: center;\r\n display: -webkit-box;\r\n display: -ms-flexbox;\r\n -js-display: flex;\r\n display: flex;\r\n margin: 0;\r\n position: relative;\r\n}\r\n\r\n.webform-submission-form .form-checkboxes .js-form-type-checkbox:not(:last-child) {\r\n margin: 0 0 8px;\r\n margin: 0 0 .5rem;\r\n}\r\n\r\n.webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n color: #091f2f;\r\n font-family: Lora,Georgia,serif;\r\n font-size: calc(16px + 4 * ((100vw - 480px) / 960));\r\n line-height: 1.2;\r\n margin: 0 7px;\r\n width: calc(100% - 42px);\r\n text-transform: none;\r\n}\r\n\r\n.webform-submission-form .ml-contact-info .js-form-type-email label,\r\n.webform-submission-form .ml-contact-info .js-form-type-textfield label,\r\n.webform-submission-form .js-form-type-checkboxes label.cb-l, \r\n.webform-submission-form .fieldset-legend {\r\n color: #091f2f;\r\n font-family: Montserrat,Arial,sans-serif;\r\n font-size: calc(14px + 2 * ((100vw - 480px) / 960));\r\n font-weight: 700;\r\n line-height: 1.4;\r\n letter-spacing: 1px;\r\n text-transform: uppercase;\r\n display: block; \r\n}\r\n.webform-submission-form ul li {\r\n background: none;\r\n}\r\n.webform-submission-form .js-webform-multiple-add {\r\n margin: 1.5rem;\r\n}\r\n\r\n.webform-submission-form .js-webform-multiple-add .form-number {\r\n padding-top: 0; \r\n}\r\n\r\n.webform-submission-form .webform-options-display-three-columns {\r\n height: 11rem; \r\n}\r\n\r\n.webform-submission-form select.error {\r\n border-color: #d22d23; \r\n}\r\n\r\n.webform-submission-data--view-mode-preview #metrolist_listing--your_contact_information summary,\r\n.webform-submission-data--view-mode-preview #metrolist_listing--select_building summary {\r\n display:none;\r\n}\r\n\r\n.hide-till-confirm {\r\n display: none;\r\n}\r\n\r\n[data-webform-wizard-current-page=\"webform_preview\"] .hide-till-confirm {\r\n display: block;\r\n}\r\n\r\n\r\n@media screen and (min-width: 768px){\r\n .webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n margin: 0 1rem;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1440px){\r\n .webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n font-size: 20px;\r\n }\r\n\r\n .webform-submission-form .ml-contact-info .js-form-type-email label,\r\n .webform-submission-form .ml-contact-info .js-form-type-textfield label,\r\n .webform-submission-form .js-form-type-checkboxes label.cb-l, \r\n .webform-submission-form .fieldset-legend { \r\n font-size: 16px;\r\n }\r\n}\r\n\r\n@media screen {\r\n .webform-submission-metrolist-listing-form .form-actions {\r\n margin-bottom: 1rem;\r\n margin-top: 1rem;\r\n }\r\n details.webform-submission-information {\r\n border: 1px solid darkgray;\r\n padding: 0 8px;\r\n }\r\n .webform-submission-information summary {\r\n color: #1871bd;\r\n }\r\n input.webform-button--submit[value=Save] {\r\n display: none;\r\n }\r\n form:not(#webform-submission-metrolist-listing-edit-form) .webform-submission-information summary::before {\r\n content: \"This form has been submitted.\";\r\n color: #d22d23;\r\n font-family: Lora,serif;\r\n display: block;\r\n } \r\n form:not(#webform-submission-metrolist-listing-edit-form) .webform-submission-information summary::after {\r\n content: \"Please request a new form to edit this property or add a new property.\";\r\n color: #58585b;\r\n font-family: Lora,serif;\r\n display: block;\r\n } \r\n form:not(#webform-submission-metrolist-listing-edit-form) .webform-submission-information span.summary::after {\r\n content: \" (click for details)\";\r\n font-size: 75%;\r\n font-family: Lora,serif;\r\n color: initial;\r\n font-style: italic;\r\n } \r\n}\r\ndiv[id^=\"metrolist_listing--i_agree2\"],\r\ninput[id^=\"edit-select-building\"],\r\ninput[id^=\"edit-your-contact-information\"],\r\ndiv[id^=\"metrolist_listing--i_agree\"] label {\r\n display:none;\r\n}\r\n\r\nlabel[for=\"edit-i-agree-agree\"] {\r\n margin: 20px 0;\r\n}\r\n\r\n#edit-i-agree--description {\r\n line-height: 35px;\r\n}\r\n\r\n#edit-actions-01 {\r\n margin-top: -3px;\r\n}\r\n\r\n.form-item-update-public-listing-information {\r\n display: none !important;\r\n}\r\n\r\n#edit-actions-01-submit {\r\n position: relative;\r\n float: right;\r\n}\r\n\r\n.webform-submission-metrolist-listing-form {\r\n display: block !important\r\n}\r\n.cb-f-wf {\r\n margin-top: 1.5rem !important;\r\n \r\n}\r\n.cb-f-wf::before {\r\n position:relative !important;\r\n top: -3px !important;\r\n left: -3px !important;\r\n}\r\n.l-wf {\r\n min-height: 18px;\r\n padding-top: 10px;\r\n}\r\n.cb-l-ws {\r\n margin: 0 10px;\r\n height: 0px;\r\n text-align: center;\r\n}\r\n.cb-dh .cb-l-ws {\r\n height: 14px;\r\n}" +css: "#seal {\r\n display:none;\r\n}\r\n\r\n.webform-submission-form .sel-c {\r\n display: block; \r\n}\r\n.webform-submission-form .webform-multiple-table td {\r\n border-bottom: solid 3px;\r\n padding-left: 1.5rem;\r\n}\r\n\r\n.webform-submission-form .form-checkboxes .js-form-type-checkbox {\r\n -webkit-box-align: center;\r\n -ms-flex-align: center;\r\n align-items: center;\r\n display: -webkit-box;\r\n display: -ms-flexbox;\r\n -js-display: flex;\r\n display: flex;\r\n margin: 0;\r\n position: relative;\r\n}\r\n\r\n.webform-submission-form .form-checkboxes .js-form-type-checkbox:not(:last-child) {\r\n margin: 0 0 8px;\r\n margin: 0 0 .5rem;\r\n}\r\n\r\n.webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n color: #091f2f;\r\n font-family: Lora,Georgia,serif;\r\n font-size: calc(16px + 4 * ((100vw - 480px) / 960));\r\n line-height: 1.2;\r\n margin: 0 7px;\r\n width: calc(100% - 42px);\r\n text-transform: none;\r\n}\r\n\r\n.webform-submission-form .ml-contact-info .js-form-type-email label,\r\n.webform-submission-form .ml-contact-info .js-form-type-textfield label,\r\n.webform-submission-form .js-form-type-checkboxes label.cb-l, \r\n.webform-submission-form .fieldset-legend {\r\n color: #091f2f;\r\n font-family: Montserrat,Arial,sans-serif;\r\n font-size: calc(14px + 2 * ((100vw - 480px) / 960));\r\n font-weight: 700;\r\n line-height: 1.4;\r\n letter-spacing: 1px;\r\n text-transform: uppercase;\r\n display: block; \r\n}\r\n.webform-submission-form ul li {\r\n background: none;\r\n}\r\n.webform-submission-form .js-webform-multiple-add {\r\n margin: 1.5rem;\r\n}\r\n\r\n.webform-submission-form .js-webform-multiple-add .form-number {\r\n padding-top: 0; \r\n}\r\n\r\n.webform-submission-form .webform-options-display-three-columns {\r\n height: 11rem; \r\n}\r\n\r\n.webform-submission-form select.error {\r\n border-color: #d22d23; \r\n}\r\n\r\n.webform-submission-data--view-mode-preview #metrolist_listing--your_contact_information summary,\r\n.webform-submission-data--view-mode-preview #metrolist_listing--select_building summary {\r\n display:none;\r\n}\r\n\r\n.hide-till-confirm {\r\n display: none;\r\n}\r\n\r\n[data-webform-wizard-current-page=\"webform_preview\"] .hide-till-confirm {\r\n display: block;\r\n}\r\n\r\n@media screen and (max-width: 767px){\r\n .webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n margin: 0 1rem;\r\n }\r\n .webform-submission-form {\r\n min-height:600px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 768px){\r\n .webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n margin: 0 1rem;\r\n }\r\n .webform-submission-form {\r\n min-height:530px;\r\n }\r\n}\r\n\r\n@media screen and (min-width: 1440px){\r\n .webform-submission-form .form-checkboxes .js-form-type-checkbox label {\r\n font-size: 20px;\r\n }\r\n\r\n .webform-submission-form .ml-contact-info .js-form-type-email label,\r\n .webform-submission-form .ml-contact-info .js-form-type-textfield label,\r\n .webform-submission-form .js-form-type-checkboxes label.cb-l, \r\n .webform-submission-form .fieldset-legend { \r\n font-size: 16px;\r\n }\r\n}\r\n\r\n@media screen {\r\n .webform-submission-metrolist-listing-form .form-actions {\r\n margin-bottom: 1rem;\r\n margin-top: 1rem;\r\n }\r\n details.webform-submission-information {\r\n border: 1px solid darkgray;\r\n padding: 0 8px;\r\n }\r\n .webform-submission-information summary {\r\n color: #1871bd;\r\n }\r\n input.webform-button--submit[value=Save] {\r\n display: none;\r\n }\r\n form:not(#webform-submission-metrolist-listing-edit-form) .webform-submission-information summary::before {\r\n content: \"This form has been submitted.\";\r\n color: #d22d23;\r\n font-family: Lora,serif;\r\n display: block;\r\n } \r\n form:not(#webform-submission-metrolist-listing-edit-form) .webform-submission-information summary::after {\r\n content: \"Please request a new form to edit this property or add a new property.\";\r\n color: #58585b;\r\n font-family: Lora,serif;\r\n display: block;\r\n } \r\n form:not(#webform-submission-metrolist-listing-edit-form) .webform-submission-information span.summary::after {\r\n content: \" (click for details)\";\r\n font-size: 75%;\r\n font-family: Lora,serif;\r\n color: initial;\r\n font-style: italic;\r\n } \r\n}\r\ndiv[id^=\"metrolist_listing--i_agree2\"],\r\ninput[id^=\"edit-select-building\"],\r\ninput[id^=\"edit-your-contact-information\"],\r\ndiv[id^=\"metrolist_listing--i_agree\"] label {\r\n display:none;\r\n}\r\n\r\nlabel[for=\"edit-i-agree-agree\"] {\r\n margin: 20px 0;\r\n}\r\n\r\n#edit-i-agree--description {\r\n line-height: 35px;\r\n}\r\n\r\n#edit-actions-01 {\r\n margin-top: -3px;\r\n}\r\n\r\n.form-item-update-public-listing-information {\r\n display: none !important;\r\n}\r\n\r\n#edit-actions-01-submit {\r\n position: relative;\r\n float: right;\r\n}\r\n\r\n.webform-submission-metrolist-listing-form {\r\n display: block !important\r\n}\r\n.cb-f-wf {\r\n margin-top: 1.5rem !important;\r\n \r\n}\r\n.cb-f-wf::before {\r\n position:relative !important;\r\n top: -3px !important;\r\n left: -3px !important;\r\n}\r\n.l-wf {\r\n min-height: 18px;\r\n padding-top: 10px;\r\n}\r\n.cb-l-ws {\r\n margin: 0 10px;\r\n height: 0px;\r\n text-align: center;\r\n}\r\n.cb-dh .cb-l-ws {\r\n height: 14px;\r\n}" javascript: '' settings: ajax: true diff --git a/config/local/samlauth.authentication.yml b/config/local/samlauth.authentication.yml index ba1f700613..acaad2c69d 100644 --- a/config/local/samlauth.authentication.yml +++ b/config/local/samlauth.authentication.yml @@ -25,10 +25,10 @@ sp_new_certificate: '' sp_private_key: '' metadata_valid_secs: 60 metadata_cache_http: false -idp_entity_id: 'urn:test:sso:boston.gov' -idp_single_sign_on_service: 'https://sso-test.boston.gov/idp/SSO.saml2' -idp_single_log_out_service: 'https://sso-test.boston.gov/idp/startSLO.ping' -idp_change_password_service: 'https://access-test.boston.gov/change-password' +idp_entity_id: 'urn:sso:boston.gov' +idp_single_sign_on_service: 'https://sso.boston.gov/idp/SSO.saml2' +idp_single_log_out_service: 'https://sso.boston.gov/idp/startSLO.ping' +idp_change_password_service: 'https://access.boston.gov/change-password' idp_certs: - 'key:iam_dir_sso_test_' idp_cert_encryption: '' diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.menu.yml b/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.menu.yml index b2745e3497..8714893f81 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.menu.yml +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.menu.yml @@ -4,3 +4,10 @@ bos_google_cloud.configForm: parent: bos_core.admin route_name: bos_google_cloud.configForm weight: 3 + +bos_google_cloud.PromptTesterForm: + title: 'Gen-AI Prompt Tester' + description: 'Prompt Tester for bos_google_cloud' + parent: bos_google_cloud.configForm + route_name: bos_google_cloud.PromptTesterForm + weight: 0 diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.task.yml b/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.task.yml index a611a77adb..64bd75cdcf 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.task.yml +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.links.task.yml @@ -3,3 +3,9 @@ bos_google_cloud.configForm: route_name: bos_google_cloud.configForm base_route: bos_core.admin weight: 2 + +bos_google_cloud.PromptTesterForm: + title: 'Prompt Tester' + route_name: bos_google_cloud.PromptTesterForm + base_route: bos_google_cloud.configForm + weight: 0 diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.routing.yml b/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.routing.yml index cecb91ddeb..6618d26bcd 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.routing.yml +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/bos_google_cloud.routing.yml @@ -6,6 +6,26 @@ bos_google_cloud.configForm: requirements: _permission: 'administer google cloud platform' +bos_google_cloud.PromptTesterForm: + path: 'admin/config/system/boston/googlecloud/prompttester' + defaults: + _form: '\Drupal\bos_google_cloud\Form\PromptTesterForm' + _title: 'Prompt Testing' + requirements: + _permission: 'administer google cloud platform' + options: + _admin_route: TRUE + +bos_google_cloud.open_PromptTesterForm: + path: 'admin/config/system/boston/googlecloud/prompttester_modal' + defaults: + _controller: '\Drupal\bos_google_cloud\Controller\PromptTesterFormController::openModalForm' + _title: 'Prompt Testing - modal' + requirements: + _permission: 'administer google cloud platform' + options: + _admin_route: TRUE + bos_google_cloud.endpoint: path: '/rest/bos_google_cloud/v1/{action}' methods: [POST,GET] diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/GcApiEndpointTester.http b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/GcApiEndpointTester.http index da0c19fd3c..d46629fb9c 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/GcApiEndpointTester.http +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/GcApiEndpointTester.http @@ -112,6 +112,18 @@ Cookie: XDEBUG_SESSION=PHPSTORM "prompt" : "poem" } +### GenAI Roll Call minute summarizer +POST https://{{host}}/rest/bos_google_cloud/v1/summarize +Content-Type: text/json +Accept: */* +Cookie: XDEBUG_SESSION=PHPSTORM + +{ + "text": "Councilor Ciommo called Docket #0547, message and order for a loan of $445,000.00 (Four Hundred Forty Five Thousand Dollars) for developing land for burial purposes and for constructing paths and avenues and embellishing the grounds in city-owned cemeteries, for the purposes of city departments including the Parks and Recreation Department, from the Committee on Ways and Means. No objection being heard, the matter was before the body. On motion of Councilor Ciommo, the order was read a second time and passed.", + "prompt": "rollcall" +} + + ### GenAI - Translate Chinese POST https://{{host}}/rest/bos_google_cloud/v1/translate Content-Type: text/json diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/PromptTesterFormController.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/PromptTesterFormController.php new file mode 100644 index 0000000000..f3ed915ec0 --- /dev/null +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Controller/PromptTesterFormController.php @@ -0,0 +1,67 @@ +formBuilder = $formBuilder; + } + + /** + * {@inheritdoc} + * + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * The Drupal service container. + * + * @return static + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('form_builder') + ); + } + + /** + * Callback for opening the modal form. + */ + public function openModalForm() { + $response = new AjaxResponse(); + + // Get the modal form using the form builder. + $modal_form = $this->formBuilder->getForm('Drupal\bos_google_cloud\Form\PromptTesterForm'); + + // Add an AJAX command to open a modal dialog with the form as the content. + $response->addCommand(new OpenModalDialogCommand('Prompt Testing/Tuning', $modal_form, ['width' => '80%'])); + + return $response; + } + +} diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/ConfigForm.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/ConfigForm.php index 0e3e971035..43d7744637 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/ConfigForm.php +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/ConfigForm.php @@ -13,6 +13,7 @@ use Drupal\bos_google_cloud\Services\GcTranslation; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; /** * Creates a config/admin form for bos_google_cloud module. @@ -95,6 +96,29 @@ public function buildForm(array $form, FormStateInterface $form_state): array { '#type' => "fieldset", '#title' => "Prompt Settings", "#description" => "The following are prompts which can be defined for the various services.", + '#description_display' => 'before', + 'tester' => [ + '#type' => "link", + '#title' => "Prompt Tester", + '#weight' => 100, + '#url' => Url::fromRoute('bos_google_cloud.open_PromptTesterForm'), + '#attributes' => [ + 'class' => ['use-ajax', 'button'], + ], + ], + ], + ], + ]; + + // Authentication section. + $authenticator = new GcAuthenticator(); + $authenticator->buildForm($form['google_cloud']['authentication_wrapper']); + + // Search section + /** + * @var $search \Drupal\bos_google_cloud\Services\GcSearch + + ], ], ] diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/PromptTesterForm.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/PromptTesterForm.php new file mode 100644 index 0000000000..de24b5f7e6 --- /dev/null +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Form/PromptTesterForm.php @@ -0,0 +1,275 @@ + "", + "summarizer" => "Summarize", + "rewriter" => "Rewrite", + "translation" => "Translate", + ]; + + /** + * {@inheritdoc} + */ + public function getFormId(): string { + return 'bos_google_cloud_PromptTesterForm'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state): array { + + if ($values = $form_state->getValues()) { + $type = $values["type"]; + $prompt = $values["prompt"]; + } + else { + $type = "select"; + $prompt = "default"; + } + $type_label = self::TYPEMAP[$type]; + + $form = [ + 'PromptTesterForm' => [ + '#tree' => FALSE, + '#type' => 'fieldset', + 'type' => [ + '#type' => 'select', + '#title' => $this->t('Select AI Action'), + '#options' => [ + 'select' => 'Select Action', + 'rewriter' => 'Rewrite', + 'summarizer' => 'Summarize', + 'translation' => 'Translate', + ], + '#default_value' => $type??"select", + '#ajax' => [ + 'event' => 'change', + 'callback' => '::ajaxCallbackPrompts', + 'wrapper' => 'edit-testdata', + ] + ], + 'testdata' => [ + '#type' => 'container', + '#attributes' => [ + 'id' => ['edit-testdata'], + 'style' => ['visibility: hidden'], + ], + 'prompt' => [ + '#type' => 'select', + '#title' => $this->t("The $type_label prompt to use"), + '#default_value' => $prompt??"default", + '#options' => $type=="select" ? [] : $this->fetchPrompts($type), + ], + 'text' => [ + '#type' => 'textarea', + '#title' => $this->t("Text to $type_label"), + '#default_value' => "Paste or input text here", + ], + 'submit' => [ + '#type' => 'button', + '#value' => ucwords($this->t($type_label)), + '#ajax' => [ + 'callback' => '::ajaxCallbackProcess', + 'wrapper' => 'edit-testresults', + ], + + ], + 'testresults' => [ + '#type' => 'container', + '#attributes' => ['id' => ['edit-testresults']], + ], + ], + ], + ]; + return $form; + } + + /** + * Ajax callback to make the input area visible. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return array + */ + public function ajaxCallbackPrompts(array $form, FormStateInterface $form_state): array { + + $values = $form_state->getValues(); + $type = $values["type"]; + $type_label = self::TYPEMAP[$type]; + + if ($type != "select") { + // Make test form visible. + unset($form['PromptTesterForm']['testdata']['#attributes']['style']); + } + + return $form['PromptTesterForm']['testdata']; + + } + + /** + * Ajax callback to run the desired test against google_cloud. + * + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + * + * @return array + * @throws \Exception + */ + public function ajaxCallbackProcess(array $form, FormStateInterface $form_state): array { + + $values = $form_state->getValues(); + $type = $values["type"]; + $type_label = self::TYPEMAP[$type]; + + switch($type) { + case "rewriter": + /** + * @var \Drupal\bos_google_cloud\Services\GcTextRewriter $processor + */ + $processor = \Drupal::service("bos_google_cloud.GcTextRewriter"); + $processor->setExpiry(GcCacheAI::CACHE_EXPIRY_NO_CACHE); + $result = $processor->execute(["text"=>$values["text"], "prompt"=>$values["prompt"]]); + break; + + case "summarizer": + /** + * @var GcTextSummarizer $processor + */ + $processor = \Drupal::service("bos_google_cloud.GcTextSummarizer"); + $processor->setExpiry(GcCacheAI::CACHE_EXPIRY_NO_CACHE); + $result = $processor->execute(["text" => $values["text"], "prompt"=> $values["prompt"]]); + break; + + case "translation": + /** + * @var \Drupal\bos_google_cloud\Services\GcTranslation $processor + */ + $processor = \Drupal::service("bos_google_cloud.GcTranslate"); + $processor->setExpiry(GcCacheAI::CACHE_EXPIRY_NO_CACHE); + $result = $processor->execute(["text"=>$values["text"], "lang"=>$values["prompt"], "prompt"=>"default"]); + break; + } + + $form["PromptTesterForm"]["testdata"] = $this->ajaxCallbackPrompts($form, $form_state); + + $result = $result ?? "No result from google_cloud (Vertex)"; + $response = $processor->response(); + $request = $processor->request(); + $fullprompt = $request["body"]["contents"][0]["parts"]; + $prompt = array_pop($fullprompt)["text"]; + $fullprompt = implode("
- ", array_column($fullprompt, "text")); + + $ai_engine = $response["ai_engine"] ?? "gemini-pro"; + + $requestSafety = []; + foreach($request["body"]["safetySettings"] as $safe) { + $requestSafety[] = "{$safe["category"]} threshold: {$safe["threshold"]}"; + } + $requestSafety = implode("
", $requestSafety); + + $responseSafety = []; + foreach($response[$ai_engine]["ratings"] as $safety) { + if (!empty($safety["category"])) { + $responseSafety[] = "{$safety["category"]} probability: {$safety["probabilityScore"]} ({$safety["probability"]})"; + $responseSafety[] = "{$safety["category"]} severity: {$safety["severityScore"]} ({$safety["severity"]})"; + } + else { + $responseSafety[] = "OVERALL probability: {$safety["probabilityScore"]} ({$safety["probability"]})"; + $responseSafety[] = "OVERALL severity: {$safety["severityScore"]} ({$safety["severity"]})"; + } + } + $responseSafety = implode("
", $responseSafety); + + $responseMetadata = []; + if ($response[$ai_engine]["usageMetadata"]) { + $responseMetadata[] = "Prompt Token Count: " . $response[$ai_engine]["usageMetadata"]["promptTokenCount"] ?? "N/A"; + $responseMetadata[] = "Candidates Token Count: " . $response[$ai_engine]["usageMetadata"]["candidatesTokenCount"] ?? "N/A"; + $responseMetadata[] = "Total Token Count: " . $response[$ai_engine]["usageMetadata"]["totalTokenCount"] ?? "N/A"; + } + $responseMetadata[] = "Analysis time: " . (intval(($response["elapsedTime"] ?? 0) * 10000) / 10000) . " sec"; + $responseMetadata = implode("
", $responseMetadata); + + $engineConfig = []; + $engineConfig[] = "Temperature: {$request["body"]["generationConfig"]["temperature"]}"; + $engineConfig[] = "Max Tokens: {$request["body"]["generationConfig"]["maxOutputTokens"]}"; + $engineConfig[] = "TopK: {$request["body"]["generationConfig"]["topK"]}"; + $engineConfig[] = "TopP: {$request["body"]["generationConfig"]["topP"]}"; + $engineConfig = implode("
", $engineConfig); + + $form["PromptTesterForm"]["testdata"]["testresults"] = [ + "#type" => "container", + '#attributes' => ['id' => ['edit-testresults']], + "output" => [ + "#type" => "fieldset", + "#title" => Markup::create("$type_label results"), + "outputtext" => [ + "#markup" => Markup::create($result) + ], + "moreinfo" => [ + "#type" => "details", + "#title" => "More Information", + "info" => [ + "#markup" => " + + + + + + + +
Action$type_label
Full Prompt$fullprompt
AI Engine$ai_engine ({$request["endpoint"]})
AI Engine Config$engineConfig
AI Post-Performance Data" . ($responseMetadata??"N/a") . "
Safety Parameters" . ($requestSafety??"N/a") . "
Reported Safety" . ($responseSafety??"N/a") . "
", + ] + ] + ] + ]; + + return $form["PromptTesterForm"]["testdata"]["testresults"]; + } + + /** + * Fetch the prompts for the indicated AI action. + * @param $type + * + * @return array + */ + private function fetchPrompts($type) { + $prompts = GcGenerationPrompt::getPrompts($type); + return $prompts; + } + + /** + * @inheritDoc + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // Not required for this test form. + } + +} diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/GcGenerationConfig.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/GcGenerationConfig.php index 1bbed93fb5..2f84ebfa25 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/GcGenerationConfig.php +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/GcGenerationConfig.php @@ -76,7 +76,7 @@ public function setTopP(float $value):GcGenerationConfig { return $this; } public function setTopK(float $value):GcGenerationConfig { - $this->config["topK"] = min(max($value,0),1); + $this->config["topK"] = min(max($value,0),40); return $this; } public function setMaxOutputTokens(float $value):GcGenerationConfig { diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextRewriter.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextRewriter.php index 30b5116dd2..7b70cf37e3 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextRewriter.php +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextRewriter.php @@ -200,18 +200,21 @@ public function execute(array $parameters = []): string { $model_id = $settings["model_id"]; + $this->response["ai_engine"] = $model_id; $this->response[$model_id]["content"] = ""; foreach ($results as $key => $result){ if (isset($result["usageMetadata"])) { - $this->response[$model_id]["usageMetadata"] = $results[0]["usageMetadata"] ?? []; + $this->response[$model_id]["usageMetadata"] = $result["usageMetadata"] ?? []; } - $this->loadSafetyRatings($result["candidates"][0]["safetyRatings"], $key, $model_id); + $this->loadSafetyRatings($result["candidates"][0]["safetyRatings"]??[], $key, $model_id); - if (isset($result["candidates"][0]["content"]["parts"][0]["text"])) { - $this->response[$model_id]["content"] .= $result["candidates"][0]["content"]["parts"][0]["text"]; + foreach($result["candidates"][0]["content"]["parts"]??[] as $part) { + if (isset($part["text"])) { + $this->response[$model_id]["content"] .= $part["text"]; + } } } diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextSummarizer.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextSummarizer.php index ea8ab837ac..2c07f3cf18 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextSummarizer.php +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTextSummarizer.php @@ -227,6 +227,7 @@ public function execute(array $parameters = []): string { $model_id = $settings["model_id"]; + $this->response["ai_engine"] = $model_id; $this->response[$model_id]["content"] = ""; foreach ($results as $key => $result){ @@ -239,8 +240,10 @@ public function execute(array $parameters = []): string { $this->loadSafetyRatings($result["candidates"][0]["safetyRatings"], $key, $model_id); } - if (isset($result["candidates"][0]["content"]["parts"][0]["text"])) { - $this->response[$model_id]["content"] .= $result["candidates"][0]["content"]["parts"][0]["text"]; + foreach($result["candidates"][0]["content"]["parts"]??[] as $part) { + if (isset($part["text"])) { + $this->response[$model_id]["content"] .= $part["text"]; + } } } diff --git a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTranslation.php b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTranslation.php index ce87f41e11..965a4f913f 100644 --- a/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTranslation.php +++ b/docroot/modules/custom/bos_components/modules/bos_google_cloud/src/Services/GcTranslation.php @@ -202,18 +202,21 @@ public function execute(array $parameters = []): string { $model_id = $settings["model_id"]; + $this->response["ai_engine"] = $model_id; $this->response[$model_id]["content"] = ""; - foreach ($results as $key => $result){ + foreach ($results as $key => $result) { if (isset($result["usageMetadata"])) { - $this->response[$model_id]["usageMetadata"] = $results[0]["usageMetadata"] ?? []; + $this->response[$model_id]["usageMetadata"] = $result["usageMetadata"] ?? []; } - $this->loadSafetyRatings($result["candidates"][0]["safetyRatings"], $key, $model_id); + $this->loadSafetyRatings($result["candidates"][0]["safetyRatings"]??[], $key, $model_id); - if (isset($result["candidates"][0]["content"]["parts"][0]["text"])) { - $this->response[$model_id]["content"] .= $result["candidates"][0]["content"]["parts"][0]["text"]; + foreach($result["candidates"][0]["content"]["parts"]??[] as $part) { + if (isset($part["text"])) { + $this->response[$model_id]["content"] .= $part["text"]; + } } } diff --git a/docroot/modules/custom/bos_content/modules/node_rollcall/src/Controller/node_rollcall_endpoints.http b/docroot/modules/custom/bos_content/modules/node_rollcall/src/Controller/node_rollcall_endpoints.http index 0ea9e174b5..d3fbadb47c 100644 --- a/docroot/modules/custom/bos_content/modules/node_rollcall/src/Controller/node_rollcall_endpoints.http +++ b/docroot/modules/custom/bos_content/modules/node_rollcall/src/Controller/node_rollcall_endpoints.http @@ -1,6 +1,14 @@ ### Load Rollcall Data -inline -# Ensure the Taxonomies vocab_votes and vocab_districts are set up first -# also need to set the config ("node_rollcall.settings.auth_token") +# 1. Ensure the Taxonomies vocab_votes and vocab_districts are set up first +# 2. Set the config ("node_rollcall.settings.auth_token") +# 3. use an array of objects, where each object contains: +# - docketId: integer unique ID for a docket +# - docket: docket ID - note: ID not unique may be re-used between years +# - subject: docket text being voted upon +# - votedate: date/time of vote in fmt yyyy-mm-ddThh:nn:ss +# - voteId: uniqueID for this vote (pk = councillor/docket) +# - councillor: The councillor surname +# - vote: typically Y, N, P, NP POST {{host}}/rest/rollcall/upload Content-Type: application/json @@ -122,21 +130,22 @@ Authorization: Bearer 8e4fcc89-0546-404a-bcf9-52695683d960 # 1. Ensure the Taxonomies vocab_votes and vocab_districts are set up first # 2. Set the config ("node_rollcall.settings.auth_token") # 3. Filepath needs to be absolute, or relative to docroot. +# 4. Can use - start to define the start record for importing (indexed from 0) +# - records to defne the #records to process POST {{host}}/rest/rollcall/upload Content-Type: application/json Authorization: Bearer 8e4fcc89-0546-404a-bcf9-52695683d960 { - "file": "sites/default/files/Rollcall_History_19Mar2024.json", - "start": 13500, - "records": 15470 + "file": "sites/default/files/Rollcall_History_19Mar2024.json" } ### Export Rollcall Data -file # NOTES: # 1. Calling without parameters will return all records in the database -# 2. Adding ?date=yyyy-mm-ddwill return all dockets for meetings after that date (can also use PHP relative date strings like “-5 days” etc) +# 2. Adding ?date=yyyy-mm-dd will return all dockets for meetings after that +# date (can also use PHP relative date strings like “-5 days” etc) # 3. Adding ?nid=1234will return all id's with a number higher than 1234 GET {{host}}/rollcall/api/v1 diff --git a/docroot/modules/custom/bos_core/src/Controllers/Curl/BosCurlControllerBase.php b/docroot/modules/custom/bos_core/src/Controllers/Curl/BosCurlControllerBase.php index df00131743..c21232e447 100644 --- a/docroot/modules/custom/bos_core/src/Controllers/Curl/BosCurlControllerBase.php +++ b/docroot/modules/custom/bos_core/src/Controllers/Curl/BosCurlControllerBase.php @@ -171,8 +171,9 @@ public function executeCurl(bool $retry = FALSE): array { **************************/ try { + $time = microtime(TRUE); $response = curl_exec($this->handle); - + $this->response["elapsedTime"] = microtime(TRUE) - $time; $this->extractHeaders( $response, $this->response["headers"]); }