Использование пакетов SDK Microsoft Web IQ для Python и TypeScript

Официальные SDK для API Microsoft Web IQ, доступные в Python и TypeScript. Эти пакеты SDK предоставляют приложениям доступ в режиме реального времени к веб-поиску, поиску видео, обзору, поиску новостей и поиску изображений — они предназначены для улучшения ответов LLM с помощью точной и актуальной информации.

Python TypeScript
Пакет pip install webiq npm install @microsoft/webiq
время выполнения Python 3.11+ Node.js 22+
Лицензия MIT MIT

Installation

Python

pip install webiq

TypeScript

npm install @microsoft/webiq

Quickstart

Руководство по Python

import os
from webiq import WebIQClient
from webiq.types import BrowseContentFormat, ContentFormat

with WebIQClient(api_key=os.environ["WEBIQ_API_KEY"]) as client:
    # Web search
    web = client.web.search(
        "Python programming",
        max_results=5,
        content_format=ContentFormat.passage,
    )
    for r in web.webResults or []:
        print(r.title, r.url)

    # Videos search
    videos = client.videos.search("machine learning tutorial", max_results=5)
    for v in videos.videoResults or []:
        print(v.title, v.length)

    # Browse a URL
    page = client.browse.fetch("https://www.microsoft.com", content_format=BrowseContentFormat.markdown)
    print(page.content)

    # News search
    news = client.news.search("technology", max_results=5)
    for item in news.newsResults or []:
        print(item.title, "-", item.source)

    # Image search
    images = client.images.search("golden gate bridge", max_results=5)
    for img in images.imageResults or []:
        print(img.title, img.url)

Руководство по TypeScript

import { WebIQClient, BrowseContentFormat, ContentFormat } from '@microsoft/webiq';

async function main() {
  const client = new WebIQClient({ apiKey: process.env.WEBIQ_API_KEY });

  // Web search
  const web = await client.web.search('TypeScript programming', {
    maxResults: 5,
    contentFormat: ContentFormat.PASSAGE,
  });
  for (const r of web.webResults ?? []) console.log(r.title, r.url);

  // Videos search
  const videos = await client.videos.search('machine learning tutorial');
  for (const v of videos.videoResults ?? []) console.log(v.title, v.length);

  // Browse a URL
  const page = await client.browse.fetch('https://www.microsoft.com', {
    contentFormat: BrowseContentFormat.MARKDOWN,
  });
  console.log(page.content);

  // News search
  const news = await client.news.search('technology', { maxResults: 5 });
  for (const item of news.newsResults ?? []) console.log(item.title, '-', item.source);

  // Image search
  const images = await client.images.search('golden gate bridge', { maxResults: 5 });
  for (const img of images.imageResults ?? []) console.log(img.title, img.url);

  await client.close();
}

main().catch(console.error);

Authentication

Ключ API

# Python
import os
from webiq import WebIQClient

client = WebIQClient(api_key=os.environ["WEBIQ_API_KEY"])
// TypeScript
import { WebIQClient } from '@microsoft/webiq';

const client = new WebIQClient({ apiKey: process.env.WEBIQ_API_KEY });

Майкрософт Ентра айди

Передайте любой объект Azure Identity TokenCredential.

# Python — uses `azure-identity`
from azure.identity import DefaultAzureCredential
from webiq import WebIQClient

client = WebIQClient(credential=DefaultAzureCredential())
// TypeScript — uses `@azure/identity`
import { DefaultAzureCredential } from '@azure/identity';
import { WebIQClient } from '@microsoft/webiq';

const client = new WebIQClient({
  credential: new DefaultAzureCredential(),
});

Ресурсы и методы ПАКЕТА SDK

Клиент предоставляет пять пространств имен ресурсов: web, videos, browse, news и images.

client.web.search() — выполните поиск в Интернете и получите содержимое страницы для обоснования.

response = client.web.search(
    query="search terms",          # Required, 1–1000 chars
    max_results=10,                # 1–50 (default 10)
    language="en",                 # ISO 639-1 code
    region="US",                   # Country/region code
    location="lat:40.7;long:-74.0",  # Optional
    content_format=ContentFormat.passage,   # passage (default) | text | html | markdown (full-page content)
    max_length=3000,               # Max content chars (default 3000)
)
# response.webResults → list of { title, url, content, lastUpdatedAt, crawledAt, language, isAdult }

client.videos.search() — Найдите соответствующее видео с необязательной поддержкой плейлистов.

response = client.videos.search(
    query="search terms",          # Required, 1–1000 chars
    max_results=30,                # 1–30 (default 30)
    language="en",
    region="US",
    enable_playlist=True,          # Include playlist results
    freshness="month",             # week | month | year
)
# response.videoResults → list of { title, url, description, length, viewCount, moments[], thumbnailUrl, ... }
# response.playlists   → list of { title, videos[] }

Просмотр

client.browse.fetch() — извлечение содержимого из любого URL-адреса. Поддерживает обход данных в реальном времени и отрисовку JavaScript.

response = client.browse.fetch(
    url="https://www.microsoft.com",   # Required
    max_length=10000,                  # Max content chars, 1–500000
    live_crawl="fallback",             # "none" (cached only, default) | "fallback" (crawl if not cached) | "force" (always crawl, bypass cache)
    include_web_links=True,            # Include outbound hyperlinks
    include_image_links=True,          # Include image URLs
    render_dynamic_pages=False,        # JS rendering (requires live_crawl="fallback")
    content_format=BrowseContentFormat.markdown,   # text | html | markdown
)
# response → { url, title, content, isAdult, lastUpdatedAt, crawledAt, retryAfter, traceId }

client.news.search() — Получите последние новостные статьи с фрагментами, наиболее релевантными запросу.

response = client.news.search(
    query="search terms",          # Required, 1–1000 chars
    max_results=10,                # 1–20 (default 10)
    language="en",
    region="US",
    location="lat:40.7;long:-74.0",  # Optional
    content_format=ContentFormat.passage,
    max_length=3000,
)
# response.newsResults → list of { title, url, content, source, lastUpdatedAt, thumbnail, isAdult }

client.images.search() — поиск изображений с фильтрацией по пропорциям, размеру и безопасному поиску.

from webiq.types import ImageAspectRatio, ImageSize, SafeSearch

response = client.images.search(
    query="search terms",          # Required, 1–1000 chars
    max_results=30,                # 1–30 (default 30)
    language="en",
    region="US",
    aspect_ratio=ImageAspectRatio.wide,   # square | wide | tall
    image_size=ImageSize.large,           # small | medium | large | extraLarge
    safe_search=SafeSearch.strict,        # off | strict
    watermark_free=True,
)
# response.imageResults → list of { title, url, hostPageUrl, caption, width, height, thumbnailUrl, ... }

Note

Методы TypeScript используют те же параметры в нотации camelCase (например, maxResults, contentFormat, liveCrawl, aspectRatio). Ознакомьтесь с кратким руководством по TypeScript выше для получения сведений о соглашении о вызовах.


Поддержка асинхронной работы

Поддержка Python

import asyncio
from webiq import WebIQAsyncClient

async def main():
    async with WebIQAsyncClient(api_key="your-api-key") as client:
        web, news = await asyncio.gather(
            client.web.search("async Python"),
            client.news.search("programming"),
        )
        for r in web.webResults or []:
            print(r.title)

asyncio.run(main())

Поддержка TypeScript

TypeScript изначально поддерживает асинхронность. Используйте Promise.all для параллелизма:

const [web, news, videos] = await Promise.all([
  client.web.search('TypeScript'),
  client.news.search('programming'),
  client.videos.search('tutorial'),
]);

Configuration

Тайм-аут и повтор

Клиент принимает timeout и retry в качестве параметров верхнего уровня. Параметр для каждого вызова timeout в каждом методе ресурса переопределяет значение по умолчанию на уровне клиента. SDK автоматически повторяет попытки 408, 500, 502, 503, 504 с экспоненциальной задержкой и джиттером; если тело ответа включает поле retryAfter (например, "30s"), SDK ждёт этой подсказки вместо задержки обратного повтора. 429 и 430никогда не повторяются автоматически — они сразу же возвращаются как RateLimitError, чтобы приложение могло применить собственную политику повторных попыток с задержкой. Время ожидания на стороне клиента также не повторяется (при них возникает APIConnectionError).

# Python
from webiq import WebIQClient, RetryPolicy

client = WebIQClient(
    api_key="your-api-key",
    timeout=10.0,                  # seconds (default: 10.0)
    retry=RetryPolicy(
        max_retries=2,             # defaults shown
        base_delay_s=0.25,
        max_delay_s=4.0,
    ),
)

# Per-call override
response = client.web.search("query", language="de", region="DE", timeout=30.0)
// TypeScript
import { WebIQClient, RetryPolicy } from '@microsoft/webiq';

const client = new WebIQClient({
  apiKey: 'your-api-key',
  timeout: 10000, // milliseconds (default: 10000)
  retry: new RetryPolicy({
    maxRetries: 2, // defaults shown
    baseDelaySeconds: 0.25,
    maxDelaySeconds: 4.0,
  }),
});

Настроенный HTTP-клиент (прокси, TLS, ...)

Для расширенных параметров HTTP — прокси, настраиваемого TLS, пула подключений и т. д. Пакет SDK не закрывает клиентские объекты, переданные вызывающей стороной.

# Python — pass a pre-configured `httpx.Client` via `http_client`
import httpx
from webiq import WebIQClient, WebIQAsyncClient

# sync
http_client = httpx.Client(
  base_url="https://api.microsoft.ai/v3",
  proxy="http://proxy:8080",
)
client = WebIQClient(api_key="your-api-key", http_client=http_client)

# async
http_client = httpx.AsyncClient(
  base_url="https://api.microsoft.ai/v3",
  proxy="http://proxy:8080",
)
client = WebIQAsyncClient(api_key="your-api-key", http_client=http_client)
// TypeScript — pass a custom `fetchFn` that wraps the global `fetch`
import { ProxyAgent } from 'undici';
import { WebIQClient } from '@microsoft/webiq';

const proxyAgent = new ProxyAgent('http://my-proxy:8080');

const client = new WebIQClient({
  apiKey: 'your-api-key',
  // Spread `init` so headers, body, and the SDK's internal `AbortSignal` are preserved.
  fetchFn: (url, init) => fetch(url, { ...init, dispatcher: proxyAgent }),
});

Отмена (TypeScript)

Каждый метод ресурса TypeScript принимает AbortSignal через options.signal. Когда сигнал прерывается, запрос в процессе выполнения отменяется, повторные попытки прекращаются, а reason сигнала повторно выбрасывается без изменений (без оболочки APIConnectionError).

// Apply a wall-clock budget that includes retries (the `timeout` option is per attempt only)
const result = await client.web.search('latest news', {
  signal: AbortSignal.timeout(5_000),
});

// Combine signals (e.g. an inbound request signal plus a wall-clock cap)
const result2 = await client.web.search('latest news', {
  signal: AbortSignal.any([req.signal, AbortSignal.timeout(5_000)]),
});

Хук телеметрии

Отслеживайте каждый запрос и каждый ответ для ведения журнала, метрик или отладки.

# Python
from webiq import WebIQClient, TelemetryEvent

def on_request(event: TelemetryEvent):
    print(f"{event.method} {event.path} → {event.status_code} ({event.elapsed_ms}ms)")

client = WebIQClient(api_key="your-api-key", telemetry_hook=on_request)
// TypeScript
import { WebIQClient, TelemetryEvent } from '@microsoft/webiq';

const client = new WebIQClient({
  apiKey: 'your-api-key',
  telemetryHook: (event: TelemetryEvent) => {
    console.log(`${event.method} ${event.path} → ${event.statusCode} (${event.elapsedMs}ms)`);
  },
});

Поля TelemetryEvent :

  • Python (snake_case): method, request_id, transport, status_code, error, attempt, trace_id, retry_after, elapsed_ms, path, error_body
  • TypeScript (camelCase): method, path, statusCode, error, elapsedMs, transport, attempt, requestId, traceId, retryAfter, errorBody

Обработка ошибок

Оба пакета SDK предоставляют типизированные исключения для структурированной обработки ошибок. Все ошибки наследуются от WebIQError.

Исключение Состояние HTTP Триггер
AuthenticationError 401 Недопустимый или отсутствующий ключ API
PermissionDeniedError 403 Аутентифицирован, но не авторизован для доступа к ресурсу
RateLimitError 429, 430 Превышен лимит запросов / превышен лимит на одновременные запросы
APIStatusError 400, 404, 500, 503, 504, ... Все остальные ошибки HTTP
APIConnectionError — Проблемы с сетью, сбои DNS, тайм-ауты
WebIQError — Базовый класс для всех ошибок пакета SDK

PermissionDeniedError и RateLimitError оба подкласса APIStatusError, так что более широкий обработчик APIStatusError по-прежнему перехватывает их. SDK не выполняет повторные попытки для ответов 429 / 430 автоматически — указание сервера по интервалу ожидания доступно через error.retry_after (Python) / error.retryAfter (TypeScript), которое ваше приложение должно обработать.

# Python
from webiq import (
    WebIQError,
    APIConnectionError,
    APIStatusError,
    AuthenticationError,
    PermissionDeniedError,
    RateLimitError,
)

try:
    response = client.web.search("test")
except PermissionDeniedError as e:
    # 403 — authenticated, but not allowed to call this resource
    print(f"Forbidden (HTTP {e.status_code}): {e}")
except AuthenticationError as e:
    # 401 — invalid or missing API key
    print(f"Auth failed (HTTP {e.status_code}): {e}")
except RateLimitError as e:
    # 429 or 430 — rate limit / concurrent-request limit (never auto-retried)
    print(f"Rate limited. Retry after: {e.retry_after}")
except APIStatusError as e:
    print(f"API error (HTTP {e.status_code}): {e}")
except APIConnectionError as e:
    print(f"Connection failed: {e}")
except WebIQError as e:
    print(f"SDK error: {e}")
// TypeScript
import {
  WebIQError,
  APIConnectionError,
  APIStatusError,
  AuthenticationError,
  PermissionDeniedError,
  RateLimitError,
} from '@microsoft/webiq';

try {
  const results = await client.web.search('test');
} catch (error) {
  if (error instanceof PermissionDeniedError) {
    // 403 — authenticated, but not allowed to call this resource
    console.error(`Forbidden (HTTP ${error.statusCode}):`, error.message);
  } else if (error instanceof AuthenticationError) {
    // 401 — invalid or missing API key
    console.error(`Auth failed (HTTP ${error.statusCode}):`, error.message);
  } else if (error instanceof RateLimitError) {
    // 429 or 430 — rate limit / concurrent-request limit (never auto-retried)
    console.error(`Rate limited. Retry after: ${error.retryAfter}`);
  } else if (error instanceof APIStatusError) {
    console.error(`API error (HTTP ${error.statusCode}):`, error.message);
  } else if (error instanceof APIConnectionError) {
    console.error('Connection failed:', error.message);
  }
}

Просмотр сведений об ошибке

Все APIStatusError экземпляры (включая PermissionDeniedError и RateLimitError) предоставляют полный текст ответа на ошибку. Большинство проблем проверки входных данных в пакете SDK Python (например, значение max_results вне диапазона, пустой query) перехватываются клиентом на стороне Pydantic и генерируется pydantic.ValidationError перед отправкой любого запроса; приведенный ниже шаблон применяется, когда сервер отклоняет запрос. browse.fetch — удобный способ вызвать такую ошибку: отсутствующие или отфильтрованные URL-адреса отображаются как структурированная ошибка:

# Python
from webiq import WebIQClient
from webiq.errors import APIStatusError

client = WebIQClient(api_key="your-api-key")

try:
    response = client.browse.fetch("https://www.not-microsoft.com")
except APIStatusError as e:
    print(f"Status: {e.status_code}")                          # e.g. 404
    print(f"Message: {e}")                                      # e.g. "No result is found"
    if isinstance(e.body, dict):
        print(f"Error code: {e.body.get('errorCode')}")        # e.g. "BrowseApiDocNotFound"
        print(f"Category: {e.body.get('errorCategory')}")      # e.g. "UserError"
        print(f"Details: {e.body.get('technicalDetails')}")    # e.g. "NotFound"
        print(f"Trace ID: {e.body.get('traceId')}")
        print(f"Retry after: {e.body.get('retryAfter')}")
// TypeScript
import { WebIQClient, APIStatusError } from '@microsoft/webiq';

async function main() {
  const client = new WebIQClient({ apiKey: 'your-api-key' });

  try {
    await client.browse.fetch('https://www.not-microsoft.com');
  } catch (error) {
    if (error instanceof APIStatusError) {
      console.error(`Status: ${error.statusCode}`); // e.g. 404
      console.error(`Message: ${error.message}`); // e.g. "No result is found"

      // Full error body from the API response
      if (error.body && typeof error.body === 'object') {
        const body = error.body as Record<string, unknown>;
        console.error(`Error code: ${body.errorCode}`); // e.g. "BrowseApiDocNotFound"
        console.error(`Category: ${body.errorCategory}`); // e.g. "UserError"
        console.error(`Details: ${body.technicalDetails}`); // e.g. "NotFound"
        console.error(`Trace ID: ${body.traceId}`); // for debugging with support
        console.error(`Retry after: ${body.retryAfter}`); // for retryable errors
      }
    }
  }
}

main().catch(console.error);

Советы по отладке

  • traceId — каждый ответ содержит поле traceId для сквозной отладки. Укажите его при обращении в службу поддержки.
  • Перехватчик телеметрии — подключите обратный вызов для журналирования каждой попытки запроса, кода состояния и времени выполнения.
  • Тело ошибки — APIStatusError.body содержит полный ответ сервера, включая errorCode, errorCategorytechnicalDetailsи traceId.