Skip to content

Commit 8af0093

Browse files
committed
Merge branch '236-langchain-mcp-export' of https://github.com/oracle-samples/ai-optimizer into 236-langchain-mcp-export
2 parents 67d0bae + 6db4100 commit 8af0093

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+982
-665
lines changed

.github/workflows/pytest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
run: |
5151
cd src/
5252
python -m pip install --upgrade pip wheel setuptools
53-
pip install torch==2.7.1+cpu -f https://download.pytorch.org/whl/cpu/torch
53+
pip install torch==2.8.0+cpu -f https://download.pytorch.org/whl/cpu/torch
5454
pip install -e ".[all-test]"
5555
5656
- name: Run All Tests

docs/content/client/configuration/model_config.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ To use OCI GenAI, the {{< short_app_ref >}} must be configured for [OCI access](
8989
>[!code]Skip the GUI!
9090
>You can set the following environment variables to automatically enable OCI GenAI models:
9191
>```shell
92-
>export OCI_GENAI_SERVICE_ENDPOINT=<OCI GenAI Service Endpoint>
92+
>export OCI_GENAI_REGION=<OCI GenAI Service Region>
9393
>export OCI_GENAI_COMPARTMENT_ID=<OCI Compartment OCID of the OCI GenAI Service>
9494
>```
9595
>
9696
>Alternatively, you can specify the following in the `~/.oci/config` configfile under the appropriate OCI profile:
9797
>```shell
98-
>service_endpoint=<OCI GenAI Service Endpoint>
99-
>compartment_id=<OCI Compartment OCID of the OCI GenAI Service>
98+
>genai_compartment_id=<OCI Compartment OCID of the OCI GenAI Service>
99+
>genai_region=<OCI GenAI Region>
100100
>```
101101
102102
{{% /tab %}}

docs/content/client/configuration/oci_config.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ Depending on the runtime environment, either [Bare Metal](#bare-metal) or [Conta
4242

4343
In addition to the standard configuration file, two additional entries are required to enable OCI GenAI Services:
4444

45-
- **service_endpoint**: the URL endpoint for the OCI GenAI Service
46-
- **compartment_id**: the compartment OCID of the OCI GenAI Service
45+
- **genai_region**: the Region for the OCI GenAI Service
46+
- **genai_compartment_id**: the Compartment OCID of the OCI GenAI Service
4747

4848
#### Bare Metal
4949

opentofu/modules/vm/templates/cloudinit-compute.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ write_files:
6565
python3.11 -m venv .venv
6666
source .venv/bin/activate
6767
pip3.11 install --upgrade pip wheel setuptools
68-
pip3.11 install torch==2.7.1+cpu -f https://download.pytorch.org/whl/cpu/torch
68+
pip3.11 install torch==2.8.0+cpu -f https://download.pytorch.org/whl/cpu/torch
6969
pip3.11 install -e ".[all]" --quiet --no-input &
7070
INSTALL_PID=$!
7171

src/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ RUN for dir in server client common; do \
2424
COPY pyproject.toml /opt/package/pyproject.toml
2525
# Use the virtual environment for pip installations
2626
RUN /opt/.venv/bin/pip install --upgrade pip wheel setuptools && \
27-
/opt/.venv/bin/pip install torch==2.7.1+cpu -f https://download.pytorch.org/whl/cpu/torch && \
27+
/opt/.venv/bin/pip install torch==2.8.0+cpu -f https://download.pytorch.org/whl/cpu/torch && \
2828
/opt/.venv/bin/pip install --no-cache-dir "/opt/package[all]"
2929

3030
##################################################

src/client/content/config/models.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@
3030
###################################
3131
# Functions
3232
###################################
33+
def clear_client_models(model_id: str) -> None:
34+
"""Clear selected models from client settings if modified"""
35+
model_keys = [
36+
("ll_model", "model"),
37+
("testbed", "judge_model"),
38+
("testbed", "qa_ll_model"),
39+
("testbed", "qa_embed_model"),
40+
]
41+
for section, key in model_keys:
42+
if state.client_settings[section][key] == model_id:
43+
state.client_settings[section][key] = None
44+
45+
3346
def get_models(force: bool = False) -> None:
3447
"""Get Models from API Server"""
3548
if force or "model_configs" not in state or not state.model_configs:
@@ -58,6 +71,9 @@ def patch_model(model: dict) -> None:
5871
"""Update Model Configuration for either Language Models or Embed Models"""
5972
_ = api_call.patch(endpoint=f"v1/models/{model['id']}", payload={"json": model})
6073
st.success(f"Model updated: {model['id']}")
74+
# If updated model is the set model and not enabled: unset the user settings
75+
if not model["enabled"]:
76+
clear_client_models(model["id"])
6177

6278

6379
def delete_model(model_id: str) -> None:
@@ -66,8 +82,7 @@ def delete_model(model_id: str) -> None:
6682
st.success(f"Model deleted: {model_id}")
6783
sleep(1)
6884
# If deleted model is the set model; unset the user settings
69-
if state.client_settings["ll_model"]["model"] == model_id:
70-
state.client_settings["ll_model"]["model"] = None
85+
clear_client_models(model_id)
7186

7287

7388
@st.dialog("Model Configuration", width="large")
@@ -93,6 +108,7 @@ def edit_model(model_type: str, action: Literal["add", "edit"], model_id: str =
93108
)
94109
api_values = get_model_apis(model_type)
95110
api_index = next((i for i, item in enumerate(api_values) if item == model["api"]), None)
111+
disable_for_oci = model["api"] in ["ChatOCIGenAI", "OCIGenAIEmbeddings"]
96112
model["api"] = st.selectbox(
97113
"API (Required):",
98114
help=help_text.help_dict["model_api"],
@@ -107,13 +123,15 @@ def edit_model(model_type: str, action: Literal["add", "edit"], model_id: str =
107123
help=help_text.help_dict["model_api_url"],
108124
key="add_model_api_url",
109125
value=model.get("url", ""),
126+
disabled=disable_for_oci
110127
)
111128
model["api_key"] = st.text_input(
112129
"API Key:",
113130
help=help_text.help_dict["model_api_key"],
114131
key="add_model_api_key",
115132
type="password",
116133
value=model.get("api_key", ""),
134+
disabled=disable_for_oci
117135
)
118136
if model_type == "ll":
119137
model["context_length"] = st.number_input(
@@ -186,7 +204,7 @@ def edit_model(model_type: str, action: Literal["add", "edit"], model_id: str =
186204

187205
def render_model_rows(model_type):
188206
"""Render rows of the models"""
189-
data_col_widths = [0.05, 0.25, 0.2, 0.28, 0.12]
207+
data_col_widths = [0.07, 0.23, 0.2, 0.28, 0.12]
190208
table_col_format = st.columns(data_col_widths, vertical_alignment="center")
191209
col1, col2, col3, col4, col5 = table_col_format
192210
col1.markdown("&#x200B;", help="Active", unsafe_allow_html=True)
@@ -198,7 +216,7 @@ def render_model_rows(model_type):
198216
model_id = model["id"]
199217
col1.text_input(
200218
"Enabled",
201-
value="✅" if model["enabled"] else "⚪",
219+
value=st_common.bool_to_emoji(model["enabled"]),
202220
key=f"{model_type}_{model_id}_enabled",
203221
label_visibility="collapsed",
204222
disabled=True,

src/client/content/config/oci.py

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# spell-checker:ignore streamlit, ocid, selectbox, genai, oraclecloud
99

1010
import inspect
11-
import re
11+
import pandas as pd
1212

1313
import streamlit as st
1414
from streamlit import session_state as state
@@ -36,7 +36,21 @@ def get_oci(force: bool = False) -> None:
3636
state.oci_configs = {}
3737

3838

39-
def patch_oci(auth_profile: str, supplied: dict, namespace: str) -> bool:
39+
def get_genai_models() -> list[dict]:
40+
"""Get Subscribed OCI Regions"""
41+
endpoint = f"v1/oci/genai/{state.client_settings['oci']['auth_profile']}"
42+
genai_models = api_call.get(endpoint=endpoint)
43+
return genai_models
44+
45+
46+
def create_genai_models() -> list[dict]:
47+
"""Create OCI GenAI Models"""
48+
endpoint = f"v1/oci/genai/{state.client_settings['oci']['auth_profile']}"
49+
genai_models = api_call.post(endpoint=endpoint)
50+
return genai_models
51+
52+
53+
def patch_oci(auth_profile: str, supplied: dict, namespace: str, toast: bool = True) -> bool:
4054
"""Update OCI"""
4155
rerun = False
4256
# Check if the OIC configuration is changed, or no namespace
@@ -49,19 +63,15 @@ def patch_oci(auth_profile: str, supplied: dict, namespace: str) -> bool:
4963
supplied["authentication"] = "security_token"
5064

5165
with st.spinner(text="Updating OCI Profile...", show_time=True):
52-
_ = api_call.patch(
53-
endpoint=f"v1/oci/{auth_profile}",
54-
payload={"json": supplied},
55-
)
66+
_ = api_call.patch(endpoint=f"v1/oci/{auth_profile}", payload={"json": supplied}, toast=toast)
5667
logger.info("OCI Profile updated: %s", auth_profile)
5768
except api_call.ApiError as ex:
5869
logger.error("OCI Update failed: %s", ex)
5970
state.oci_error = ex
6071
st_common.clear_state_key("oci_configs")
61-
if supplied.get("service_endpoint"):
62-
st_common.clear_state_key("model_configs")
6372
else:
64-
st.toast("No Changes Detected.", icon="ℹ️")
73+
if toast:
74+
st.toast("No Changes Detected.", icon="ℹ️")
6575

6676
return rerun
6777

@@ -155,31 +165,54 @@ def main() -> None:
155165
OCI Authentication must be configured above.
156166
""")
157167
with st.container(border=True):
158-
supplied["compartment_id"] = st.text_input(
168+
if "genai_models" not in state:
169+
state.genai_models = []
170+
supplied["genai_compartment_id"] = st.text_input(
159171
"OCI GenAI Compartment OCID:",
160-
value=oci_lookup[selected_oci_auth_profile]["compartment_id"],
172+
value=oci_lookup[selected_oci_auth_profile]["genai_compartment_id"],
161173
placeholder="Compartment OCID for GenAI Services",
162174
key="oci_genai_compartment_id",
163175
disabled=not namespace,
164176
)
165-
match = re.search(
166-
r"\.([a-zA-Z\-0-9]+)\.oci\.oraclecloud\.com", oci_lookup[selected_oci_auth_profile]["service_endpoint"]
167-
)
168-
supplied["service_endpoint"] = match.group(1) if match else None
169-
supplied["service_endpoint"] = st.text_input(
170-
"OCI GenAI Region:",
171-
value=oci_lookup[selected_oci_auth_profile]["service_endpoint"],
172-
help="Region of GenAI Service",
173-
key="oci_genai_region",
174-
disabled=not namespace,
175-
)
176-
177-
if st.button("Save OCI GenAI", key="save_oci_genai", disabled=not namespace):
178-
if not (supplied["compartment_id"] and supplied["service_endpoint"]):
179-
st.error("All fields are required.", icon="🛑")
177+
if st.button("Check for OCI GenAI Models", key="check_oci_genai", disabled=not namespace):
178+
if not supplied["genai_compartment_id"]:
179+
st.error("OCI GenAI Compartment OCID is required.", icon="🛑")
180180
st.stop()
181-
if patch_oci(selected_oci_auth_profile, supplied, namespace):
182-
st.rerun()
181+
with st.spinner("Looking for OCI GenAI Models... please be patient.", show_time=True):
182+
patch_oci(selected_oci_auth_profile, supplied, namespace, toast=False)
183+
state.genai_models = get_genai_models()
184+
if state.genai_models:
185+
regions = list({item["region"] for item in state.genai_models if "region" in item})
186+
supplied["genai_region"] = st.selectbox(
187+
"Region:",
188+
regions,
189+
key="selected_genai_region",
190+
)
191+
filtered_models = [
192+
m
193+
for m in state.genai_models
194+
if m["region"] == supplied["genai_region"]
195+
and ("CHAT" in m["capabilities"] or "TEXT_EMBEDDINGS" in m["capabilities"])
196+
]
197+
table_data = []
198+
for m in filtered_models:
199+
table_data.append(
200+
{
201+
"Model Name": m["model_name"],
202+
"Large Language": st_common.bool_to_emoji("CHAT" in m["capabilities"]),
203+
"Embedding": st_common.bool_to_emoji("TEXT_EMBEDDINGS" in m["capabilities"]),
204+
}
205+
)
206+
207+
# Convert to DataFrame and display
208+
df = pd.DataFrame(table_data)
209+
st.dataframe(df, hide_index=True)
210+
if st.button("Enable Region Models", key="enable_oci_region_models", type="primary"):
211+
with st.spinner("Enabling OCI GenAI Models... please be patient.", show_time=True):
212+
patch_oci(selected_oci_auth_profile, supplied, namespace, toast=False)
213+
_ = create_genai_models()
214+
st_common.clear_state_key("model_configs")
215+
st.success("Oracle GenAI models - Enabled.", icon="✅")
183216

184217

185218
if __name__ == "__main__" or "page.py" in inspect.stack()[1].filename:

src/client/content/config/settings.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,9 @@ def apply_uploaded_settings(uploaded):
139139
logger.error("%s Settings Update failed: %s", state.client_settings["client"], ex)
140140

141141

142-
def spring_ai_conf_check(ll_model, embed_model) -> str:
142+
def spring_ai_conf_check(ll_model: dict, embed_model: dict) -> str:
143143
"""Check if configuration is valid for SpringAI package"""
144-
if ll_model is None or embed_model is None:
144+
if not ll_model or not embed_model:
145145
return "hybrid"
146146

147147
ll_api = ll_model["api"]
@@ -317,18 +317,24 @@ def main():
317317
st.header("Export source code templates", divider="red")
318318
# Merge the User Settings into the Model Config
319319
model_lookup = st_common.state_configs_lookup("model_configs", "id")
320-
ll_config = model_lookup[state.client_settings["ll_model"]["model"]] | state.client_settings["ll_model"]
321-
embed_config = (
322-
model_lookup[state.client_settings["vector_search"]["model"]] | state.client_settings["vector_search"]
323-
)
320+
try:
321+
ll_config = model_lookup[state.client_settings["ll_model"]["model"]] | state.client_settings["ll_model"]
322+
except KeyError:
323+
ll_config = {}
324+
try:
325+
embed_config = (
326+
model_lookup[state.client_settings["vector_search"]["model"]] | state.client_settings["vector_search"]
327+
)
328+
except KeyError:
329+
embed_config = {}
324330
spring_ai_conf = spring_ai_conf_check(ll_config, embed_config)
325331

326332
if spring_ai_conf == "hybrid":
327333
st.markdown(f"""
328334
The current configuration combination of embedding and language models
329335
is currently **not supported** for SpringAI.
330-
- Language Model: **{ll_config["model"]}**
331-
- Embedding Model: **{embed_config["model"]}**
336+
- Language Model: **{ll_config.get("model", "Unset")}**
337+
- Embedding Model: **{embed_config.get("model", "Unset")}**
332338
""")
333339
else:
334340
col_left, col_centre, _ = st.columns([3, 4, 3])

src/client/content/testbed.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,14 @@ def create_gauge(value):
8181
# Get the Report
8282
if eid:
8383
report = api_call.get(endpoint="v1/testbed/evaluation", params={"eid": eid})
84-
8584
# Settings
8685
st.subheader("Evaluation Settings")
8786
ll_settings = pd.DataFrame(report["settings"]["ll_model"], index=[0])
88-
ll_settings.drop(["streaming", "chat_history"], axis=1, inplace=True)
87+
ll_settings.drop(["streaming", "chat_history", "context_length"], axis=1, inplace=True)
8988
ll_settings_reversed = ll_settings.iloc[:, ::-1]
9089
st.dataframe(ll_settings_reversed, hide_index=True)
90+
if report["settings"]["testbed"]["judge_model"]:
91+
st.markdown(f"**Judge Model**: {report['settings']['testbed']['judge_model']}")
9192
if report["settings"]["vector_search"]["enabled"]:
9293
st.subheader("Vector Search Settings")
9394
st.markdown(f"""**Database**: {report["settings"]["database"]["alias"]};
@@ -364,18 +365,29 @@ def main():
364365
max_value=100,
365366
value=2,
366367
)
368+
if state.client_settings["testbed"].get("qa_ll_model") is None:
369+
state.client_settings["testbed"]["qa_ll_model"] = available_embed_models[0]
370+
selected_qa_ll_model = state.client_settings["testbed"]["qa_ll_model"]
371+
qa_ll_model_idx = available_embed_models.index(selected_qa_ll_model)
367372
test_gen_llm = col_center.selectbox(
368373
"Q&A Language Model:",
369374
key="selected_test_gen_llm",
370375
options=available_ll_models,
371-
index=0,
376+
index=qa_ll_model_idx,
377+
on_change=st_common.update_client_settings("testbed"),
372378
help="Don't see your model? Unfortunately it is not currently supported by the testing framework.",
373379
)
380+
381+
if state.client_settings["testbed"].get("qa_embed_model") is None:
382+
state.client_settings["testbed"]["qa_embed_model"] = available_embed_models[0]
383+
selected_qa_embed_model = state.client_settings["testbed"]["qa_embed_model"]
384+
qa_embed_model_idx = available_embed_models.index(selected_qa_embed_model)
374385
test_gen_embed = col_right.selectbox(
375386
"Q&A Embedding Model:",
376387
key="selected_test_gen_embed",
377388
options=available_embed_models,
378-
index=0,
389+
index=qa_embed_model_idx,
390+
on_change=st_common.update_client_settings("testbed"),
379391
help="Don't see your model? Unfortunately it is not currently supported by the testing framework.",
380392
)
381393
api_params = {
@@ -396,7 +408,6 @@ def main():
396408

397409
# Load TestSets (and Evaluations if from DB)
398410
if col_left.button(button_text, key="load_tests", use_container_width=True, disabled=state.running):
399-
placeholder = st.empty()
400411
with st.spinner("Processing Q&A... please be patient.", show_time=True):
401412
if testset_source != "Database":
402413
api_params["name"] = (state.testbed["testset_name"],)
@@ -521,12 +532,17 @@ def main():
521532
st_common.vector_search_sidebar()
522533
st.write("Choose a model to judge the correctness of the chatbot answer, then start evaluation.")
523534
col_left, col_center, _ = st.columns([3, 3, 4])
535+
if state.client_settings["testbed"].get("judge_model") is None:
536+
state.client_settings["testbed"]["judge_model"] = available_ll_models[0]
537+
selected_judge = state.client_settings["testbed"]["judge_model"]
538+
judge_idx = available_ll_models.index(selected_judge)
524539
col_left.selectbox(
525540
"Judge Language Model:",
526-
key="selected_evaluate_judge",
541+
key="selected_testbed_judge_model",
527542
options=available_ll_models,
528-
index=0,
543+
index=judge_idx,
529544
label_visibility="collapsed",
545+
on_change=st_common.update_client_settings("testbed"),
530546
)
531547
if col_center.button(
532548
"Start Evaluation",
@@ -539,7 +555,7 @@ def main():
539555
st_common.clear_state_key("testbed_evaluations")
540556
st_common.patch_settings()
541557
endpoint = "v1/testbed/evaluate"
542-
api_params = {"tid": state.testbed["testset_id"], "judge": state.selected_evaluate_judge}
558+
api_params = {"tid": state.testbed["testset_id"], "judge": state.selected_testbed_judge_model}
543559
evaluate = api_call.post(endpoint=endpoint, params=api_params, timeout=1200)
544560
st.success("Evaluation Complete!", icon="✅")
545561

0 commit comments

Comments
 (0)