Skip to content

Commit a8f4f9c

Browse files
authored
feat: migrate from httpx to niquests (#275)
Signed-off-by: Anupam Kumar <kyteinsky@gmail.com>
1 parent d02edd3 commit a8f4f9c

12 files changed

Lines changed: 112 additions & 248 deletions

File tree

.github/workflows/integration-test.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
matrix:
6464
php-versions: [ '8.2' ]
6565
databases: [ 'pgsql' ]
66-
server-versions: [ 'stable32', 'master' ]
66+
server-versions: [ 'stable32', 'stable33', 'master' ]
6767

6868
name: Integration test on ${{ matrix.server-versions }} php@${{ matrix.php-versions }}
6969

@@ -192,9 +192,10 @@ jobs:
192192
193193
- name: Register backend
194194
run: |
195-
./occ app_api:daemon:register --net host manual_install "Manual Install" manual-install http localhost http://localhost:8080
196-
./occ app_api:app:register context_chat_backend manual_install --json-info "{\"appid\":\"context_chat_backend\",\"name\":\"Context Chat Backend\",\"daemon_config_name\":\"manual_install\",\"version\":\"${{ fromJson(steps.appinfo.outputs.result).version }}\",\"secret\":\"12345\",\"port\":10034,\"scopes\":[],\"system_app\":0}" --force-scopes --wait-finish
195+
timeout 10 ./occ app_api:daemon:register --net host manual_install "Manual Install" manual-install http localhost http://localhost:8080
196+
timeout 120 ./occ app_api:app:register context_chat_backend manual_install --json-info "{\"appid\":\"context_chat_backend\",\"name\":\"Context Chat Backend\",\"daemon_config_name\":\"manual_install\",\"version\":\"${{ fromJson(steps.appinfo.outputs.result).version }}\",\"secret\":\"12345\",\"port\":10034,\"scopes\":[],\"system_app\":0}" --force-scopes --wait-finish
197197
ls -la context_chat_backend/persistent_storage/*
198+
sleep 30 # Wait for the em server to get ready
198199
199200
- name: Scan files, baseline
200201
run: |

appinfo/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Setup background job workers as described here: https://docs.nextcloud.com/serve
2929
<bugs>https://github.com/nextcloud/context_chat_backend/issues</bugs>
3030
<repository type="git">https://github.com/nextcloud/context_chat_backend.git</repository>
3131
<dependencies>
32-
<nextcloud min-version="32" max-version="33"/>
32+
<nextcloud min-version="32" max-version="34"/>
3333
</dependencies>
3434
<external-app>
3535
<docker-install>

config.cpu.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
debug: true
44
uvicorn_log_level: info
55
disable_aaa: false
6-
httpx_verify_ssl: true
6+
verify_ssl: true
77
use_colors: true
88
uvicorn_workers: 1
99
embedding_chunk_size: 2000

config.gpu.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
debug: true
44
uvicorn_log_level: info
55
disable_aaa: false
6-
httpx_verify_ssl: true
6+
verify_ssl: true
77
use_colors: true
88
uvicorn_workers: 1
99
embedding_chunk_size: 2000

context_chat_backend/config_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def get_config(file_path: str) -> TConfig:
106106
debug=config.get('debug', False),
107107
uvicorn_log_level=config.get('uvicorn_log_level', 'info'),
108108
disable_aaa=config.get('disable_aaa', False),
109-
httpx_verify_ssl=config.get('httpx_verify_ssl', True),
109+
verify_ssl=config.get('verify_ssl', config.get('httpx_verify_ssl', True)),
110110
use_colors=config.get('use_colors', True),
111111
uvicorn_workers=config.get('uvicorn_workers', 1),
112112
embedding_chunk_size=config.get('embedding_chunk_size', 1000),

context_chat_backend/controller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from .config_parser import get_config
4141
from .dyn_loader import LLMModelLoader, VectorDBLoader
4242
from .models.types import LlmException
43-
from .ocs_utils import AppAPIAuthMiddleware
43+
from nc_py_api.ex_app import AppAPIAuthMiddleware
4444
from .utils import JSONResponse, exec_in_proc, is_valid_provider_id, is_valid_source_id, value_of
4545
from .vectordb.service import (
4646
count_documents_by_provider,

context_chat_backend/models/nc_texttotext.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import time
77
from typing import Any
88

9-
import httpx
9+
import niquests
1010
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
1111
from langchain_core.language_models.llms import LLM
1212
from nc_py_api import NextcloudApp, NextcloudException
@@ -83,15 +83,15 @@ def _call(
8383
)
8484
break
8585
except NextcloudException as e:
86-
if e.status_code == httpx.codes.PRECONDITION_FAILED:
86+
if e.status_code == niquests.codes.precondition_failed: # pyright: ignore[reportAttributeAccessIssue]
8787
raise LlmException(
8888
'Failed to schedule Nextcloud TaskProcessing task: '
8989
'This app is setup to use a text generation provider in Nextcloud. '
9090
'No such provider is installed on Nextcloud instance. '
9191
'Please install integration_openai, llm2 or any other text2text provider.'
9292
) from e
9393

94-
if e.status_code == httpx.codes.TOO_MANY_REQUESTS:
94+
if e.status_code == niquests.codes.too_many_requests: # pyright: ignore[reportAttributeAccessIssue]
9595
logger.warning('Rate limited during task scheduling, waiting 10s before retrying')
9696
time.sleep(10)
9797
continue
@@ -116,17 +116,15 @@ def _call(
116116
try:
117117
response = nc.ocs('GET', f'/ocs/v1.php/taskprocessing/task/{task.id}')
118118
except (
119-
httpx.RemoteProtocolError,
120-
httpx.ReadError,
121-
httpx.LocalProtocolError,
122-
httpx.PoolTimeout,
119+
niquests.exceptions.ConnectionError,
120+
niquests.exceptions.Timeout,
123121
) as e:
124122
logger.warning('Ignored error during task polling', exc_info=e)
125123
time.sleep(5)
126124
i += 1
127125
continue
128126
except NextcloudException as e:
129-
if e.status_code == httpx.codes.TOO_MANY_REQUESTS:
127+
if e.status_code == niquests.codes.too_many_requests: # pyright: ignore[reportAttributeAccessIssue]
130128
logger.warning('Rate limited during task polling, waiting 10s before retrying')
131129
time.sleep(10)
132130
i += 2

context_chat_backend/network_em.py

Lines changed: 26 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
# SPDX-License-Identifier: AGPL-3.0-or-later
44
#
55
import logging
6-
from collections.abc import Generator
76
from time import sleep
87
from typing import Literal, TypedDict
98

10-
import httpx
9+
import niquests
1110
from langchain_core.embeddings import Embeddings
1211
from pydantic import BaseModel
1312

@@ -42,15 +41,6 @@ class CreateEmbeddingResponse(TypedDict):
4241
usage: EmbeddingUsage
4342

4443

45-
class ApiKeyAuth(httpx.Auth):
46-
def __init__(self, apikey: str | bytes) -> None:
47-
self._apikey = apikey
48-
49-
def auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]:
50-
request.headers['Authorization'] = f'Bearer {self._apikey}'
51-
yield request
52-
53-
5444
class NetworkEmbeddings(Embeddings, BaseModel):
5545
app_config: TConfig
5646

@@ -66,43 +56,46 @@ def _get_embedding(self, input_: str | list[str], try_: int = 3) -> list[float]
6656
try:
6757
match emconf.auth:
6858
case None:
69-
auth = httpx.USE_CLIENT_DEFAULT
59+
auth = None
7060
case TEmbeddingAuthApiKey(apikey=apikey):
71-
auth = ApiKeyAuth(apikey=apikey)
61+
auth = niquests.auth.BearerTokenAuth(token=apikey) # pyright: ignore[reportAttributeAccessIssue]
7262
case TEmbeddingAuthBasic(username=username, password=password):
73-
auth = httpx.BasicAuth(username=username, password=password)
63+
auth = niquests.auth.HTTPBasicAuth(username=username, password=password) # pyright: ignore[reportAttributeAccessIssue]
7464

7565
data = {'input': input_}
7666
if emconf.model_name:
7767
data['model'] = emconf.model_name
7868

79-
with httpx.Client(verify=self.app_config.httpx_verify_ssl) as client:
80-
response = client.post(
81-
f'{emconf.base_url.removesuffix("/")}/embeddings',
82-
json=data,
83-
timeout=emconf.request_timeout,
84-
auth=auth,
85-
)
86-
if response.status_code // 100 == 4:
87-
raise FatalEmbeddingException(response.text)
88-
if response.status_code // 100 != 2:
89-
raise EmbeddingException(response.text)
69+
response = niquests.post(
70+
f'{emconf.base_url.removesuffix("/")}/embeddings',
71+
json=data,
72+
timeout=emconf.request_timeout,
73+
auth=auth,
74+
verify=self.app_config.verify_ssl,
75+
)
76+
if response.status_code is None:
77+
raise EmbeddingException('Error: no response from embedding service')
78+
if response.status_code // 100 == 4:
79+
raise FatalEmbeddingException(response.text)
80+
if response.status_code // 100 != 2:
81+
raise EmbeddingException(response.text)
9082
except FatalEmbeddingException as e:
9183
logger.error('Fatal error while getting embeddings: %s', str(e), exc_info=e)
9284
raise e
93-
except (
94-
EmbeddingException,
95-
httpx.RemoteProtocolError,
96-
httpx.ReadError,
97-
httpx.LocalProtocolError,
98-
httpx.PoolTimeout,
99-
) as e:
85+
except EmbeddingException as e:
10086
if try_ > 0:
10187
logger.debug('Retrying embedding request in 5 secs', extra={'try': try_})
10288
sleep(5)
10389
return self._get_embedding(input_, try_ - 1)
10490
raise RetryableEmbeddingException('Error: request to get embeddings failed') from e
105-
except httpx.ConnectError as e:
91+
except niquests.exceptions.Timeout as e:
92+
if try_ > 0:
93+
logger.debug('Timeout while getting embeddings, retrying in 5 secs', extra={'try': try_})
94+
sleep(5)
95+
return self._get_embedding(input_, try_ - 1)
96+
logger.error('Timeout while getting embeddings', exc_info=e)
97+
raise EmbeddingException('Error: timeout while getting embeddings') from e
98+
except niquests.exceptions.ConnectionError as e:
10699
if self.app_config.embedding.workers > 0:
107100
logger.error(
108101
'Error connecting to the embedding server, check if it is running and the logs',
@@ -111,13 +104,6 @@ def _get_embedding(self, input_: str | list[str], try_: int = 3) -> list[float]
111104
raise EmbeddingException('Error: failed to connect to the embedding service') from e
112105
logger.error('Error connecting to the remote embedding service', exc_info=e)
113106
raise EmbeddingException('Error: failed to connect to the remote embedding service') from e
114-
except httpx.NetworkError as e:
115-
if try_ > 0:
116-
logger.debug('Network error while getting embeddings, retrying in 5 secs', extra={'try': try_})
117-
sleep(5)
118-
return self._get_embedding(input_, try_ - 1)
119-
logger.error('Network error while getting embeddings', exc_info=e)
120-
raise EmbeddingException('Error: network error while getting embeddings') from e
121107
except Exception as e:
122108
logger.error('Unexpected error while getting embeddings', exc_info=e)
123109
raise EmbeddingException('Error: unexpected error while getting embeddings') from e

context_chat_backend/ocs_utils.py

Lines changed: 0 additions & 143 deletions
This file was deleted.

context_chat_backend/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class TConfig(BaseModel):
3838
debug: bool
3939
uvicorn_log_level: str
4040
disable_aaa: bool
41-
httpx_verify_ssl: bool
41+
verify_ssl: bool
4242
use_colors: bool
4343
uvicorn_workers: int
4444
embedding_chunk_size: int

0 commit comments

Comments
 (0)