Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions LICENSE.certifi
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
This package contains a modified version of ca-bundle.crt:

ca-bundle.crt -- Bundle of CA Root Certificates

This is a bundle of X.509 certificates of public Certificate Authorities
(CA). These were automatically extracted from Mozilla's root certificates
file (certdata.txt). This file can be found in the mozilla source tree:
https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt
It contains the certificates in PEM format and therefore
can be directly used with curl / libcurl / php_curl, or with
an Apache+mod_ssl webserver for SSL client authentication.
Just configure this file as the SSLCACertificateFile.#

***** BEGIN LICENSE BLOCK *****
This Source Code Form is subject to the terms of the Mozilla Public License,
v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain
one at http://mozilla.org/MPL/2.0/.

***** END LICENSE BLOCK *****
@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
include dashscope/resources/qwen.tiktoken
include NOTICE
include LICENSE.certifi
3 changes: 3 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This product depends on certifi (https://github.com/certifi/python-certifi),
which is provided under the Mozilla Public License 2.0.
See bundled LICENSE.certifi for details.
22 changes: 14 additions & 8 deletions dashscope/api_entities/http_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,10 @@ async def _handle_aio_request(self): # pylint: disable=too-many-branches
if should_close:
await session.close()
except Exception as e:
logger.debug(e)
raise e
logger.error(f"Async request failed: {e}", exc_info=True)
from dashscope.common.error import DashScopeException

raise DashScopeException(str(e)) from e

@staticmethod
def __handle_parameters(params: dict) -> dict:
Expand Down Expand Up @@ -411,10 +413,12 @@ def _handle_response( # pylint: disable=too-many-branches
request_id=request_id,
status_code=status_code,
output=None,
code=msg["code"]
if "code" in msg
else None, # noqa E501
message=msg["message"] if "message" in msg else None,
code=msg.get("code")
or msg.get("error_code")
or f"http_{status_code}",
message=msg.get("message")
or msg.get("error_message")
or f"HTTP {status_code} error",
headers=headers,
) # noqa E501
else:
Expand Down Expand Up @@ -517,5 +521,7 @@ def _handle_request(self): # pylint: disable=too-many-branches
if should_close:
session.close()
except Exception as e:
logger.debug(e)
raise e
logger.error(f"Sync request failed: {e}", exc_info=True)
from dashscope.common.error import DashScopeException

raise DashScopeException(str(e)) from e
34 changes: 26 additions & 8 deletions dashscope/api_entities/websocket_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ async def aio_call(self):
pass
return result

async def connection_handler(self): # pylint: disable=too-many-branches
async def connection_handler(
self,
): # pylint: disable=too-many-branches,too-many-statements
try:
task_id = None
async with aiohttp.ClientSession(
Expand Down Expand Up @@ -203,26 +205,42 @@ async def connection_handler(self): # pylint: disable=too-many-branches
)
except aiohttp.WSServerHandshakeError as e:
code = e.status
msg = e.message
original_msg = e.message or ""

if e.status in [HTTPStatus.FORBIDDEN, HTTPStatus.UNAUTHORIZED]:
msg = "Unauthorized, your api-key is invalid!"
friendly_hint = "Unauthorized, your api-key may be invalid!"
msg = (
f"{friendly_hint} (Server details: {original_msg})"
if original_msg
else friendly_hint
)
elif e.status == HTTPStatus.SERVICE_UNAVAILABLE:
msg = SERVICE_503_MESSAGE
friendly_hint = SERVICE_503_MESSAGE
msg = (
f"{friendly_hint} (Server details: {original_msg})"
if original_msg
else friendly_hint
)
else:
pass
msg = (
original_msg
or f"WebSocket handshake failed with status {e.status}"
)

yield DashScopeAPIResponse(
request_id=task_id,
status_code=code,
code=code,
code=f"http_{code}" if code else "websocket_handshake_error",
message=msg,
)
except BaseException as e:
logger.exception(e)
exception_name = type(e).__name__
yield DashScopeAPIResponse(
request_id="",
status_code=-1,
code="Unknown",
message=f"Error type: {type(e)}, message: {e}",
code="",
message=f"[SDK Internal Error] {exception_name}: {e}",
)

def _to_DashScopeAPIResponse(self, task_id, is_binary, result):
Expand Down
50 changes: 49 additions & 1 deletion dashscope/audio/http_tts/http_speech_synthesizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@


class HttpSpeechSynthesisResult:
"""The result of HTTP speech synthesis."""
"""The result of HTTP speech synthesis.

This class wraps the actual SpeechSynthesisResponse and provides
convenient access to both synthesis-specific data and standard
DashScopeAPIResponse attributes for compatibility with CLI tools.
"""

def __init__(
self,
Expand Down Expand Up @@ -62,6 +67,49 @@ def response(self) -> Optional[SpeechSynthesisResponse]:
"""Get the full API response."""
return self._response

# Proxy standard DashScopeAPIResponse attributes for CLI compatibility
@property
def request_id(self) -> str:
"""Get the request ID from the underlying response."""
if self._response:
return self._response.request_id
return ""

@property
def status_code(self) -> int:
"""Get the HTTP status code from the underlying response."""
if self._response:
return self._response.status_code
return 0

@property
def code(self) -> str:
"""Get the error code from the underlying response."""
if self._response:
return self._response.code
return ""

@property
def message(self) -> str:
"""Get the error message from the underlying response."""
if self._response:
return self._response.message
return ""

@property
def output(self):
"""Get the output from the underlying response."""
if self._response:
return self._response.output
return None

@property
def usage(self):
"""Get the usage from the underlying response."""
if self._response:
return self._response.usage
return None


class HttpSpeechSynthesizer(BaseApi):
"""HTTP-based text-to-speech interface for CosyVoice."""
Expand Down
6 changes: 0 additions & 6 deletions dashscope/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from dashscope.common.error import AuthenticationError # noqa: E402
from dashscope.cli import ( # noqa: E402
application,
code_generation,
deployments,
embeddings,
files,
Expand All @@ -39,7 +38,6 @@
speech_synthesis,
tokenization,
transcription,
understanding,
video_synthesis,
)

Expand Down Expand Up @@ -97,9 +95,7 @@
"embeddings",
"tokenization",
"models",
"understanding",
"application",
"code-generation",
"image-synthesis",
"video-synthesis",
"image-generation",
Expand Down Expand Up @@ -276,9 +272,7 @@ def callback(
app.add_typer(embeddings.app)
app.add_typer(tokenization.app)
app.add_typer(models.app)
app.add_typer(understanding.app)
app.add_typer(application.app)
app.add_typer(code_generation.app)
app.add_typer(image_synthesis.app)
app.add_typer(video_synthesis.app)
app.add_typer(image_generation.app)
Expand Down
71 changes: 64 additions & 7 deletions dashscope/cli/agentic_rl.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,15 +536,27 @@ def run(
# Handle API response errors
if result.status_code != 200:
raise OutputError(
f"API returned status {result.status_code}:"
f" {result.message}",
(
f"API error [status={result.status_code}, "
f"code={result.code}]: {result.message}"
),
response={
"status_code": result.status_code,
"code": result.code,
"message": result.message,
"request_id": result.request_id,
},
)

progress.update(
task,
description="[green]✅ Job submitted successfully![/green]",
)

# Validate output is not None before accessing attributes
if result.output is None:
raise OutputError("API returned success but output is empty")

format_output(
{
"job_id": result.output.job_id,
Expand Down Expand Up @@ -594,9 +606,22 @@ def get(
# Handle API response errors
if result.status_code != 200:
raise OutputError(
f"API returned status {result.status_code}: {result.message}",
(
f"API error [status={result.status_code}, "
f"code={result.code}]: {result.message}"
),
response={
"status_code": result.status_code,
"code": result.code,
"message": result.message,
"request_id": result.request_id,
},
)

# Validate output is not None before accessing attributes
if result.output is None:
raise OutputError("API returned success but output is empty")

format_output(
{
"job_id": result.output.job_id,
Expand Down Expand Up @@ -628,7 +653,16 @@ def cancel(
# Handle API response errors
if result.status_code != 200:
raise OutputError(
f"API returned status {result.status_code}: {result.message}",
(
f"API error [status={result.status_code}, "
f"code={result.code}]: {result.message}"
),
response={
"status_code": result.status_code,
"code": result.code,
"message": result.message,
"request_id": result.request_id,
},
)

err_console.print(
Expand Down Expand Up @@ -666,11 +700,25 @@ def logs(
# Handle API response errors
if result.status_code != 200:
raise OutputError(
f"API returned status {result.status_code}: {result.message}",
(
f"API error [status={result.status_code}, "
f"code={result.code}]: {result.message}"
),
response={
"status_code": result.status_code,
"code": result.code,
"message": result.message,
"request_id": result.request_id,
},
)

# Validate output is not None before accessing attributes
logs_data = ""
if result.output is not None and isinstance(result.output, dict):
logs_data = result.output.get("logs", "")

format_output(
{"job_id": job_id, "logs": getattr(result.output, "logs", "")},
{"job_id": job_id, "logs": logs_data},
fmt=output_format,
)
except Exception as e:
Expand Down Expand Up @@ -703,7 +751,16 @@ def list_jobs(
# Handle API response errors
if result.status_code != 200:
raise OutputError(
f"API returned status {result.status_code}: {result.message}",
(
f"API error [status={result.status_code}, "
f"code={result.code}]: {result.message}"
),
response={
"status_code": result.status_code,
"code": result.code,
"message": result.message,
"request_id": result.request_id,
},
)

output_data = serialize_for_output(
Expand Down
5 changes: 3 additions & 2 deletions dashscope/cli/code_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def callback(ctx: typer.Context):
typer.echo(ctx.get_help())


@app.command("create")
@app.command("create", hidden=True)
@handle_sdk_error("Code generation request failed")
def create(
model: str = typer.Option(..., "-m", "--model", help="The model to call"),
Expand Down Expand Up @@ -56,7 +56,8 @@ def create(
),
n: int = typer.Option(1, "-n", "--n", help="The number of output results"),
):
"""Call code generation API."""
"""[DEPRECATED] No independent endpoint available.
This command is hidden and will be removed."""
messages = [UserRoleMessageParam(content=content)]
if attachment_meta is not None:
try:
Expand Down
Loading
Loading