Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

community: fix some features on Naver ChatModel & embedding model 2 #29243

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
28 changes: 14 additions & 14 deletions docs/docs/integrations/chat/naver.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,24 @@
"\n",
"## Setup\n",
"\n",
"Before using the chat model, you must go through the three steps below.\n",
"Before using the chat model, you must go through the four steps below.\n",
"\n",
"1. Creating [NAVER Cloud Platform](https://www.ncloud.com/) account \n",
"2. Apply to use [CLOVA Studio](https://www.ncloud.com/product/aiService/clovaStudio)\n",
"3. Find API Keys after creating CLOVA Studio Test App or Service App (See [here](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#테스트앱생성).)\n",
"3. Create a CLOVA Studio Test App or Service App of a model to use (See [here](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#테스트앱생성).)\n",
"4. Issue a Test or Service API key (See [here](https://api.ncloud-docs.com/docs/ai-naver-clovastudio-summary#API%ED%82%A4).)\n",
"\n",
"### Credentials\n",
"\n",
"CLOVA Studio requires 2 keys (`NCP_CLOVASTUDIO_API_KEY` and `NCP_APIGW_API_KEY`).\n",
" - `NCP_CLOVASTUDIO_API_KEY` is issued per Test App or Service App\n",
" - `NCP_APIGW_API_KEY` is issued per account, could be optional depending on the region you are using\n",
"\n",
"The two API Keys could be found by clicking `App Request Status` > `Service App, Test App List` > `‘Details’ button for each app` in [CLOVA Studio](https://clovastudio.ncloud.com/studio-application/service-app)\n",
"Set the `NCP_CLOVASTUDIO_API_KEY` environment variable with your API key.\n",
" - Note that if you are using a legacy API Key (that doesn't start with `nv-*` prefix), you might need to get an additional API Key by clicking `App Request Status` > `Service App, Test App List` > `‘Details’ button for each app` in [CLOVA Studio](https://clovastudio.ncloud.com/studio-application/service-app) and set it as `NCP_APIGW_API_KEY`.\n",
"\n",
"You can add them to your environment variables as below:\n",
"\n",
"``` bash\n",
"export NCP_CLOVASTUDIO_API_KEY=\"your-api-key-here\"\n",
"export NCP_APIGW_API_KEY=\"your-api-key-here\"\n",
"# Uncomment below to use a legacy API key\n",
"# export NCP_APIGW_API_KEY=\"your-api-key-here\"\n",
"```"
]
},
Expand All @@ -71,10 +70,11 @@
" os.environ[\"NCP_CLOVASTUDIO_API_KEY\"] = getpass.getpass(\n",
" \"Enter your NCP CLOVA Studio API Key: \"\n",
" )\n",
"if not os.getenv(\"NCP_APIGW_API_KEY\"):\n",
" os.environ[\"NCP_APIGW_API_KEY\"] = getpass.getpass(\n",
" \"Enter your NCP API Gateway API key: \"\n",
" )"
"# Uncomment below to use a legacy API key\n",
"# if not os.getenv(\"NCP_APIGW_API_KEY\"):\n",
"# os.environ[\"NCP_APIGW_API_KEY\"] = getpass.getpass(\n",
"# \"Enter your NCP API Gateway API key: \"\n",
"# )"
]
},
{
Expand Down Expand Up @@ -340,7 +340,7 @@
"\n",
"When going live with production-level application using CLOVA Studio, you should apply for and use Service App. (See [here](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#서비스앱신청).)\n",
"\n",
"For a Service App, a corresponding `NCP_CLOVASTUDIO_API_KEY` is issued and can only be called with it."
"For a Service App, you should use a corresponding Service API key and can only be called with it."
]
},
{
Expand All @@ -353,7 +353,7 @@
"# Update environment variables\n",
"\n",
"os.environ[\"NCP_CLOVASTUDIO_API_KEY\"] = getpass.getpass(\n",
" \"Enter NCP CLOVA Studio API Key for Service App: \"\n",
" \"Enter NCP CLOVA Studio Service API Key: \"\n",
")"
]
},
Expand Down
3 changes: 2 additions & 1 deletion docs/docs/integrations/providers/naver.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ Please refer to [NCP User Guide](https://guide.ncloud-docs.com/docs/clovastudio-

## Installation and Setup

- Get both CLOVA Studio API Key and API Gateway Key by [creating your app](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#create-test-app) and set them as environment variables respectively (`NCP_CLOVASTUDIO_API_KEY`, `NCP_APIGW_API_KEY`).
- Get a CLOVA Studio API Key by [issuing it](https://api.ncloud-docs.com/docs/ai-naver-clovastudio-summary#API%ED%82%A4) and set it as an environment variable (`NCP_CLOVASTUDIO_API_KEY`).
- If you are using a legacy API Key (that doesn't start with `nv-*` prefix), you might need to get an additional API Key by [creating your app](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#create-test-app) and set it as `NCP_APIGW_API_KEY`.
- Install the integration Python package with:

```bash
Expand Down
33 changes: 19 additions & 14 deletions docs/docs/integrations/text_embedding/naver.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,13 @@
"\n",
"1. Creating [NAVER Cloud Platform](https://www.ncloud.com/) account \n",
"2. Apply to use [CLOVA Studio](https://www.ncloud.com/product/aiService/clovaStudio)\n",
"3. Find API Keys after creating CLOVA Studio Test App or Service App (See [here](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#테스트앱생성).)\n",
"3. Create a CLOVA Studio Test App or Service App of a model to use (See [here](https://guide.ncloud-docs.com/docs/clovastudio-explorer03#%ED%85%8C%EC%8A%A4%ED%8A%B8%EC%95%B1%EC%83%9D%EC%84%B1).)\n",
"4. Issue a Test or Service API key (See [here](https://api.ncloud-docs.com/docs/ai-naver-clovastudio-summary#API%ED%82%A4).)\n",
"\n",
"### Credentials\n",
"\n",
"CLOVA Studio requires 3 keys (`NCP_CLOVASTUDIO_API_KEY`, `NCP_APIGW_API_KEY` and `NCP_CLOVASTUDIO_APP_ID`) for embeddings.\n",
"- `NCP_CLOVASTUDIO_API_KEY` and `NCP_CLOVASTUDIO_APP_ID` is issued per serviceApp or testApp\n",
"- `NCP_APIGW_API_KEY` is issued per account\n",
"\n",
"The two API Keys could be found by clicking `App Request Status` > `Service App, Test App List` > `‘Details’ button for each app` in [CLOVA Studio](https://clovastudio.ncloud.com/studio-application/service-app)."
"Set the `NCP_CLOVASTUDIO_API_KEY` environment variable with your API key.\n",
" - Note that if you are using a legacy API Key (that doesn't start with `nv-*` prefix), you might need two additional keys to be set as environment variables (`NCP_APIGW_API_KEY` and `NCP_CLOVASTUDIO_APP_ID`. They could be found by clicking `App Request Status` > `Service App, Test App List` > `Details` button for each app in [CLOVA Studio](https://clovastudio.ncloud.com/studio-application/service-app)."
]
},
{
Expand All @@ -56,9 +54,15 @@
"if not os.getenv(\"NCP_CLOVASTUDIO_API_KEY\"):\n",
" os.environ[\"NCP_CLOVASTUDIO_API_KEY\"] = getpass.getpass(\n",
" \"Enter NCP CLOVA Studio API Key: \"\n",
" )\n",
"if not os.getenv(\"NCP_APIGW_API_KEY\"):\n",
" os.environ[\"NCP_APIGW_API_KEY\"] = getpass.getpass(\"Enter NCP API Gateway API Key: \")"
" )"
]
},
{
"cell_type": "markdown",
"id": "b31fc062",
"metadata": {},
"source": [
"Uncomment below to use a legacy API key:"
]
},
{
Expand All @@ -68,7 +72,9 @@
"metadata": {},
"outputs": [],
"source": [
"os.environ[\"NCP_CLOVASTUDIO_APP_ID\"] = input(\"Enter NCP CLOVA Studio App ID: \")"
"# if not os.getenv(\"NCP_APIGW_API_KEY\"):\n",
"# os.environ[\"NCP_APIGW_API_KEY\"] = getpass.getpass(\"Enter NCP API Gateway API Key: \")\n",
"# os.environ[\"NCP_CLOVASTUDIO_APP_ID\"] = input(\"Enter NCP CLOVA Studio App ID: \")"
]
},
{
Expand Down Expand Up @@ -118,8 +124,7 @@
"from langchain_community.embeddings import ClovaXEmbeddings\n",
"\n",
"embeddings = ClovaXEmbeddings(\n",
" model=\"clir-emb-dolphin\", # set with the model name of corresponding app id. Default is `clir-emb-dolphin`\n",
" # app_id=\"...\" # set if you prefer to pass app id directly instead of using environment variables\n",
" model=\"clir-emb-dolphin\" # set with the model name of corresponding app id. Default is `clir-emb-dolphin`\n",
")"
]
},
Expand Down Expand Up @@ -251,7 +256,7 @@
"\n",
"When going live with production-level application using CLOVA Studio, you should apply for and use Service App. (See [here](https://guide.ncloud-docs.com/docs/en/clovastudio-playground01#서비스앱신청).)\n",
"\n",
"For a Service App, corresponding `NCP_CLOVASTUDIO_API_KEY` and `NCP_CLOVASTUDIO_APP_ID` are issued and can only be called with them."
"For a Service App, you should use a corresponding Service API key and can only be called with it."
]
},
{
Expand All @@ -266,6 +271,7 @@
"os.environ[\"NCP_CLOVASTUDIO_API_KEY\"] = getpass.getpass(\n",
" \"Enter NCP CLOVA Studio API Key for Service App: \"\n",
")\n",
"# Uncomment below to use a legacy API key:\n",
"os.environ[\"NCP_CLOVASTUDIO_APP_ID\"] = input(\"Enter NCP CLOVA Studio Service App ID: \")"
]
},
Expand All @@ -279,7 +285,6 @@
"embeddings = ClovaXEmbeddings(\n",
" service_app=True,\n",
" model=\"clir-emb-dolphin\", # set with the model name of corresponding app id of your Service App\n",
" # app_id=\"...\" # set if you prefer to pass app id directly instead of using environment variables\n",
")"
]
},
Expand Down
93 changes: 67 additions & 26 deletions libs/community/langchain_community/chat_models/naver.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)

import httpx
from httpx_sse import SSEError
from langchain_core.callbacks import (
AsyncCallbackManagerForLLMRun,
CallbackManagerForLLMRun,
Expand All @@ -35,7 +36,13 @@
)
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult
from langchain_core.utils import convert_to_secret_str, get_from_env
from pydantic import AliasChoices, ConfigDict, Field, SecretStr, model_validator
from pydantic import (
AliasChoices,
ConfigDict,
Field,
SecretStr,
model_validator,
)
from typing_extensions import Self

_DEFAULT_BASE_URL = "https://clovastudio.stream.ntruss.com"
Expand All @@ -47,16 +54,13 @@ def _convert_chunk_to_message_chunk(
sse: Any, default_class: Type[BaseMessageChunk]
) -> BaseMessageChunk:
sse_data = sse.json()
message = sse_data.get("message")
role = message.get("role")
content = message.get("content") or ""

if sse.event == "result":
response_metadata = {}
if "stopReason" in sse_data:
response_metadata["stopReason"] = sse_data["stopReason"]
response_metadata = _sse_data_to_response_metadata(sse_data)
return AIMessageChunk(content="", response_metadata=response_metadata)

message = sse_data.get("message")
role = message.get("role")
content = message.get("content") or ""
if role == "user" or default_class == HumanMessageChunk:
return HumanMessageChunk(content=content)
elif role == "assistant" or default_class == AIMessageChunk:
Expand All @@ -69,6 +73,21 @@ def _convert_chunk_to_message_chunk(
return default_class(content=content) # type: ignore[call-arg]


def _sse_data_to_response_metadata(sse_data: Dict) -> Dict[str, Any]:
response_metadata = {}
if "stopReason" in sse_data:
response_metadata["stop_reason"] = sse_data["stopReason"]
if "inputLength" in sse_data:
response_metadata["input_length"] = sse_data["inputLength"]
if "outputLength" in sse_data:
response_metadata["output_length"] = sse_data["outputLength"]
if "seed" in sse_data:
response_metadata["seed"] = sse_data["seed"]
if "aiFilter" in sse_data:
response_metadata["ai_filter"] = sse_data["aiFilter"]
return response_metadata


def _convert_message_to_naver_chat_message(
message: BaseMessage,
) -> Dict:
Expand Down Expand Up @@ -130,6 +149,8 @@ async def _aiter_sse(
event_data = sse.json()
if sse.event == "signal" and event_data.get("data", {}) == "[DONE]":
return
if sse.event == "error":
raise SSEError(message=sse.data)
yield sse


Expand Down Expand Up @@ -240,10 +261,15 @@ def _identifying_params(self) -> Dict[str, Any]:

@property
def lc_secrets(self) -> Dict[str, str]:
return {
"ncp_clovastudio_api_key": "NCP_CLOVASTUDIO_API_KEY",
"ncp_apigw_api_key": "NCP_APIGW_API_KEY",
}
if not self._is_new_api_key():
return {
"ncp_clovastudio_api_key": "NCP_CLOVASTUDIO_API_KEY",
}
else:
return {
"ncp_clovastudio_api_key": "NCP_CLOVASTUDIO_API_KEY",
"ncp_apigw_api_key": "NCP_APIGW_API_KEY",
}

@property
def _llm_type(self) -> str:
Expand Down Expand Up @@ -285,10 +311,8 @@ def validate_model_after(self) -> Self:
get_from_env("ncp_clovastudio_api_key", "NCP_CLOVASTUDIO_API_KEY")
)

if not self.ncp_apigw_api_key:
self.ncp_apigw_api_key = convert_to_secret_str(
get_from_env("ncp_apigw_api_key", "NCP_APIGW_API_KEY", "")
)
if not self._is_new_api_key():
self._init_fields_on_old_api_key()

if not self.base_url:
self.base_url = get_from_env(
Expand All @@ -311,6 +335,18 @@ def validate_model_after(self) -> Self:

return self

def _is_new_api_key(self) -> bool:
if self.ncp_clovastudio_api_key:
return self.ncp_clovastudio_api_key.get_secret_value().startswith("nv-")
else:
return False

def _init_fields_on_old_api_key(self) -> None:
if not self.ncp_apigw_api_key:
self.ncp_apigw_api_key = convert_to_secret_str(
get_from_env("ncp_apigw_api_key", "NCP_APIGW_API_KEY", "")
)

def default_headers(self) -> Dict[str, Any]:
headers = {
"Content-Type": "application/json",
Expand All @@ -322,16 +358,22 @@ def default_headers(self) -> Dict[str, Any]:
if self.ncp_clovastudio_api_key
else None
)
if clovastudio_api_key:
headers["X-NCP-CLOVASTUDIO-API-KEY"] = clovastudio_api_key

apigw_api_key = (
self.ncp_apigw_api_key.get_secret_value()
if self.ncp_apigw_api_key
else None
)
if apigw_api_key:
headers["X-NCP-APIGW-API-KEY"] = apigw_api_key
if self._is_new_api_key():
### headers on new api key
headers["Authorization"] = f"Bearer {clovastudio_api_key}"
else:
### headers on old api key
if clovastudio_api_key:
headers["X-NCP-CLOVASTUDIO-API-KEY"] = clovastudio_api_key

apigw_api_key = (
self.ncp_apigw_api_key.get_secret_value()
if self.ncp_apigw_api_key
else None
)
if apigw_api_key:
headers["X-NCP-APIGW-API-KEY"] = apigw_api_key

return headers

Expand All @@ -348,7 +390,6 @@ def _create_message_dicts(
def _completion_with_retry(self, **kwargs: Any) -> Any:
from httpx_sse import (
ServerSentEvent,
SSEError,
connect_sse,
)

Expand Down
Loading
Loading