deploy: 5c7567584d
|
|
@ -1,4 +1,4 @@
|
|||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 416c1b7f738dbd8ff231cf7246bbbb27
|
||||
config: e029b4806150eb62aeaf9741fac54bfc
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
|
|
|||
1
CNAME
|
|
@ -1 +0,0 @@
|
|||
docs.archgw.com
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
from flask import Flask, request, jsonify
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from langchain.memory import ConversationBufferMemory
|
||||
from langchain.schema import AIMessage, HumanMessage
|
||||
from langchain import OpenAI
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Global dictionary to keep track of user memories
|
||||
user_memories = {}
|
||||
|
||||
def get_user_conversation(user_id):
|
||||
"""
|
||||
Retrieve the user's conversation memory using LangChain.
|
||||
If the user does not exist, initialize their conversation memory.
|
||||
"""
|
||||
if user_id not in user_memories:
|
||||
user_memories[user_id] = ConversationBufferMemory(return_messages=True)
|
||||
return user_memories[user_id]
|
||||
|
||||
def update_user_conversation(user_id, client_messages, intent_changed):
|
||||
"""
|
||||
Update the user's conversation memory with new messages using LangChain.
|
||||
Each message is augmented with a UUID, timestamp, and intent change marker.
|
||||
Only new messages are added to avoid duplication.
|
||||
"""
|
||||
memory = get_user_conversation(user_id)
|
||||
stored_messages = memory.chat_memory.messages
|
||||
|
||||
# Determine the number of stored messages
|
||||
num_stored_messages = len(stored_messages)
|
||||
new_messages = client_messages[num_stored_messages:]
|
||||
|
||||
# Process each new message
|
||||
for index, message in enumerate(new_messages):
|
||||
role = message.get('role')
|
||||
content = message.get('content')
|
||||
metadata = {
|
||||
'uuid': str(uuid.uuid4()),
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'intent_changed': False # Default value
|
||||
}
|
||||
|
||||
# Mark the intent change on the last message if detected
|
||||
if intent_changed and index == len(new_messages) - 1:
|
||||
metadata['intent_changed'] = True
|
||||
|
||||
# Create a new message with metadata
|
||||
if role == 'user':
|
||||
memory.chat_memory.add_message(
|
||||
HumanMessage(content=content, additional_kwargs={'metadata': metadata})
|
||||
)
|
||||
elif role == 'assistant':
|
||||
memory.chat_memory.add_message(
|
||||
AIMessage(content=content, additional_kwargs={'metadata': metadata})
|
||||
)
|
||||
else:
|
||||
# Handle other roles if necessary
|
||||
pass
|
||||
|
||||
return memory
|
||||
|
||||
def get_messages_since_last_intent(messages):
|
||||
"""
|
||||
Retrieve messages from the last intent change onwards using LangChain.
|
||||
"""
|
||||
messages_since_intent = []
|
||||
for message in reversed(messages):
|
||||
# Insert message at the beginning to maintain correct order
|
||||
messages_since_intent.insert(0, message)
|
||||
metadata = message.additional_kwargs.get('metadata', {})
|
||||
# Break if intent_changed is True
|
||||
if metadata.get('intent_changed', False) == True:
|
||||
break
|
||||
return messages_since_intent
|
||||
|
||||
def forward_to_llm(messages):
|
||||
"""
|
||||
Forward messages to an upstream LLM using LangChain.
|
||||
"""
|
||||
# Convert messages to a conversation string
|
||||
conversation = ""
|
||||
for message in messages:
|
||||
role = 'User' if isinstance(message, HumanMessage) else 'Assistant'
|
||||
content = message.content
|
||||
conversation += f"{role}: {content}\n"
|
||||
# Use LangChain's LLM to get a response. This call is proxied through Arch for end-to-end observability and traffic management
|
||||
llm = OpenAI()
|
||||
# Create a prompt that includes the conversation
|
||||
prompt = f"{conversation}Assistant:"
|
||||
response = llm(prompt)
|
||||
return response
|
||||
|
||||
@app.route('/process_rag', methods=['POST'])
|
||||
def process_rag():
|
||||
# Extract JSON data from the request
|
||||
data = request.get_json()
|
||||
|
||||
user_id = data.get('user_id')
|
||||
if not user_id:
|
||||
return jsonify({'error': 'User ID is required'}), 400
|
||||
|
||||
client_messages = data.get('messages')
|
||||
if not client_messages or not isinstance(client_messages, list):
|
||||
return jsonify({'error': 'Messages array is required'}), 400
|
||||
|
||||
# Extract the intent change marker from Arch's headers if present for the current prompt
|
||||
intent_changed_header = request.headers.get('x-arch-intent-marker', '').lower()
|
||||
if intent_changed_header in ['', 'false']:
|
||||
intent_changed = False
|
||||
elif intent_changed_header == 'true':
|
||||
intent_changed = True
|
||||
else:
|
||||
# Invalid value provided
|
||||
return jsonify({'error': 'Invalid value for x-arch-prompt-intent-change header'}), 400
|
||||
|
||||
# Update user conversation based on intent change
|
||||
memory = update_user_conversation(user_id, client_messages, intent_changed)
|
||||
|
||||
# Retrieve messages since last intent change for LLM
|
||||
messages_for_llm = get_messages_since_last_intent(memory.chat_memory.messages)
|
||||
|
||||
# Forward messages to upstream LLM
|
||||
llm_response = forward_to_llm(messages_for_llm)
|
||||
|
||||
# Prepare the messages to return
|
||||
messages_to_return = []
|
||||
for message in memory.chat_memory.messages:
|
||||
role = 'user' if isinstance(message, HumanMessage) else 'assistant'
|
||||
content = message.content
|
||||
metadata = message.additional_kwargs.get('metadata', {})
|
||||
message_entry = {
|
||||
'uuid': metadata.get('uuid'),
|
||||
'timestamp': metadata.get('timestamp'),
|
||||
'role': role,
|
||||
'content': content,
|
||||
'intent_changed': metadata.get('intent_changed', False)
|
||||
}
|
||||
messages_to_return.append(message_entry)
|
||||
|
||||
# Prepare the response
|
||||
response = {
|
||||
'user_id': user_id,
|
||||
'messages': messages_to_return,
|
||||
'llm_response': llm_response
|
||||
}
|
||||
|
||||
return jsonify(response), 200
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
version: "0.1-beta"
|
||||
listener:
|
||||
address: 127.0.0.1 | 0.0.0.0
|
||||
port_value: 8080 #If you configure port 443, you'll need to update the listener with tls_certificates
|
||||
messages: tuple | hugging-face-messages-api
|
||||
|
||||
system_prompts:
|
||||
- name: network_assistant
|
||||
content: You are a network assistant that just offers facts about the operational health of the network
|
||||
|
||||
llm_providers:
|
||||
- name: "OpenAI"
|
||||
access_key: $OPEN_AI_KEY
|
||||
model: gpt-4o
|
||||
default: true
|
||||
- name: "Mistral"
|
||||
access_key: $MISTRAL_KEY
|
||||
model: mixtral8-7B
|
||||
|
||||
prompt_endpoints:
|
||||
- "http://127.0.0.2"
|
||||
- "http://127.0.0.1"
|
||||
|
||||
prompt_guards:
|
||||
input-guard:
|
||||
- name: #jailbreak
|
||||
on-exception-message: Looks like you are curious about my abilities. But I can only
|
||||
|
||||
prompt_targets:
|
||||
- name: information_extraction
|
||||
type: RAG
|
||||
description: this prompt handles all information extractions scenarios
|
||||
path: /agent/summary
|
||||
|
||||
- name: reboot_network_device
|
||||
path: /agent/action
|
||||
description: used to help network operators with perform device operations like rebooting a device.
|
||||
parameters:
|
||||
error_target: #handle errors from Bolt or upstream LLMs
|
||||
name: “error_handler”
|
||||
path: /errors
|
||||
|
Before Width: | Height: | Size: 297 KiB After Width: | Height: | Size: 297 KiB |
|
|
@ -1,13 +0,0 @@
|
|||
Configuration Reference
|
||||
============================
|
||||
|
||||
The following is a complete reference of the ``prompt-conifg.yml`` that controls the behavior of a single instance of
|
||||
the Arch gateway. We've kept things simple (less than 80 lines) and held off on exposing additional functionality (for
|
||||
e.g. suppporting push observability stats, managing prompt-endpoints as virtual cluster, exposing more load balancing
|
||||
options, etc). Our belief that the simple things, should be simple. So we offert good defaults for developers, so
|
||||
that they can spend more of their time in building features unique to their AI experience.
|
||||
|
||||
.. literalinclude:: /_config/prompt-config-full-reference.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:caption: :download:`prompt-config-full-reference-beta-1-0.yml </_config/prompt-config-full-reference.yml>`
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
.. _getting_started:
|
||||
|
||||
Getting Started
|
||||
================
|
||||
|
||||
.. sidebar:: Pre-requisites
|
||||
|
||||
In order for you to get started, please make sure that `Docker <https://www.docker.com/get-started>`_
|
||||
and `Python <https://www.python.org/downloads/>`_ are installed locally.
|
||||
|
||||
As the examples use the pre-built `Arch Docker images <https://hub.docker.com/r/katanemo/arch>`_,
|
||||
they should work on the following architectures:
|
||||
|
||||
- x86_64
|
||||
- ARM 64
|
||||
|
||||
|
||||
This section gets you started with a very simple configuration and provides some example configurations.
|
||||
|
||||
|
||||
The fastest way to get started using Arch is installing `pre-built binaries <https://hub.docker.com/r/katanemo/arch>`_.
|
||||
You can also build it from source.
|
||||
|
||||
Step 1: Install the Arch CLI
|
||||
----------------------------
|
||||
Arch's CLI allows you to manage and interact with the Arch gateway efficiently. To install the CLI, simply
|
||||
run the following command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install archgw
|
||||
|
||||
This will install the archgw command-line tool globally on your system.
|
||||
|
||||
Step 2: Start Arch Gateway
|
||||
--------------------------
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
archgw up --quick-start
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Today, only support a static bootstrap configuration file for simplicity today:
|
||||
|
||||
.. literalinclude:: /_config/getting-started.yml
|
||||
:language: yaml
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Use Cases
|
||||
|
||||
use_cases/rag
|
||||
use_cases/function_calling
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
.. _arch_function_calling_agentic_guide:
|
||||
|
||||
Agentic (Text-to-Action) Apps
|
||||
==============================
|
||||
|
||||
Arch helps you easily personalize your applications by calling application-specific (API) functions
|
||||
via user prompts. This involves any predefined functions or APIs you want to expose to users to perform tasks,
|
||||
gather information, or manipulate data. This capability is generally referred to as **function calling**, where
|
||||
you have the flexibility to support “agentic” apps tailored to specific use cases - from updating insurance
|
||||
claims to creating ad campaigns - via prompts.
|
||||
|
||||
Arch analyzes prompts, extracts critical information from prompts, engages in lightweight conversation with
|
||||
the user to gather any missing parameters and makes API calls so that you can focus on writing business logic.
|
||||
Arch does this via its purpose-built :ref:`Arch-FC LLM <llms_in_arch>` - the fastest (200ms p90 - 10x faser than GPT-4o)
|
||||
and cheapest (100x than GPT-40) function-calling LLM that matches performance with frontier models.
|
||||
______________________________________________________________________________________________
|
||||
|
||||
.. image:: /_static/img/function-calling-network-flow.jpg
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
|
||||
Single Function Call
|
||||
--------------------
|
||||
In the most common scenario, users will request a single action via prompts, and Arch efficiently processes the
|
||||
request by extracting relevant parameters, validating the input, and calling the designated function or API. Here
|
||||
is how you would go about enabling this scenario with Arch:
|
||||
|
||||
Step 1: Define prompt targets with functions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. literalinclude:: /_config/function-calling-network-agent.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:emphasize-lines: 16-37
|
||||
:caption: Define prompt targets that can enable users to engage with API and backened functions of an app
|
||||
|
||||
Step 2: Process request parameters in Flask
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once the prompt targets are configured as above, handling those parameters is
|
||||
|
||||
.. literalinclude:: /_include/parameter_handling_flask.py
|
||||
:language: python
|
||||
:linenos:
|
||||
:caption: Flask API example for parameter extraction via HTTP request parameters
|
||||
|
||||
Parallel/ Multiple Function Calling
|
||||
-----------------------------------
|
||||
In more complex use cases, users may request multiple actions or need multiple APIs/functions to be called
|
||||
simultaneously or sequentially. With Arch, you can handle these scenarios efficiently using parallel or multiple
|
||||
function calling. This allows your application to engage in a broader range of interactions, such as updating
|
||||
different datasets, triggering events across systems, or collecting results from multiple services in one prompt.
|
||||
|
||||
Arch-FC1B is built to manage these parallel tasks efficiently, ensuring low latency and high throughput, even
|
||||
when multiple functions are invoked. It provides two mechanisms to handle these cases:
|
||||
|
||||
Step 1: Define Multiple Function Targets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When enabling multiple function calling, define the prompt targets in a way that supports multiple functions or
|
||||
API calls based on the user's prompt. These targets can be triggered in parallel or sequentially, depending on
|
||||
the user's intent.
|
||||
|
||||
Example of Multiple Prompt Targets in YAML:
|
||||
|
||||
.. literalinclude:: /_config/function-calling-network-agent.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:emphasize-lines: 16-37
|
||||
:caption: Define prompt targets that can enable users to engage with API and backened functions of an app
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
.. _arch_rag_guide:
|
||||
|
||||
Retrieval-Augmented (RAG)
|
||||
=========================
|
||||
|
||||
The following section describes how Arch can help you build faster, smarter and more accurate
|
||||
Retrieval-Augmented Generation (RAG) applications.
|
||||
|
||||
Intent-drift Detection
|
||||
----------------------
|
||||
|
||||
Developers struggle to handle `follow-up <https://www.reddit.com/r/ChatGPTPromptGenius/comments/17dzmpy/how_to_use_rag_with_conversation_history_for/?>`_
|
||||
or `clarifying <https://www.reddit.com/r/LocalLLaMA/comments/18mqwg6/best_practice_for_rag_with_followup_chat/>`_
|
||||
questions. Specifically, when users ask for changes or additions to previous responses their AI applications often
|
||||
generate entirely new responses instead of adjusting previous ones. Arch offers *intent-drift* tracking as a feature so
|
||||
that developers can know when the user has shifted away from a previous intent so that they can dramatically improve
|
||||
retrieval accuracy, lower overall token cost and improve the speed of their responses back to users.
|
||||
|
||||
Arch uses its built-in lightweight NLI and embedding models to know if the user has steered away from an active intent.
|
||||
Arch's intent-drift detection mechanism is based on its' *prompt_targets* primtive. Arch tries to match an incoming
|
||||
prompt to one of the *prompt_targets* configured in the gateway. Once it detects that the user has moved away from an active
|
||||
active intent, Arch adds the ``x-arch-intent-drift`` headers to the request before sending it your application servers.
|
||||
|
||||
.. literalinclude:: /_include/intent_detection.py
|
||||
:language: python
|
||||
:linenos:
|
||||
:lines: 95-125
|
||||
:emphasize-lines: 14-22
|
||||
:caption: :download:`Intent drift detection in python </_include/intent_detection.py>`
|
||||
|
||||
_____________________________________________________________________________________________________________________
|
||||
|
||||
.. Note::
|
||||
|
||||
Arch is (mostly) stateless so that it can scale in an embarrassingly parrallel fashion. So, while Arch offers
|
||||
intent-drift detetction, you still have to maintain converational state with intent drift as meta-data. The
|
||||
following code snippets show how easily you can build and enrich conversational history with Langchain (in python),
|
||||
so that you can use the most relevant prompts for your retrieval and for prompting upstream LLMs.
|
||||
|
||||
|
||||
Step 1: define ConversationBufferMemory
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. literalinclude:: /_include/intent_detection.py
|
||||
:language: python
|
||||
:linenos:
|
||||
:lines: 1-21
|
||||
|
||||
Step 2: update ConversationBufferMemory w/ intent
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. literalinclude:: /_include/intent_detection.py
|
||||
:language: python
|
||||
:linenos:
|
||||
:lines: 22-62
|
||||
|
||||
Step 3: get Messages based on latest drift
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. literalinclude:: /_include/intent_detection.py
|
||||
:language: python
|
||||
:linenos:
|
||||
:lines: 64-76
|
||||
|
||||
|
||||
You can used the last set of messages that match to an intent to prompt an LLM, use it with an vector-DB for
|
||||
improved retrieval, etc. With Arch and a few lines of code, you can improve the retrieval accuracy, lower overall
|
||||
token cost and dramatically improve the speed of their responses back to users.
|
||||
|
||||
Parameter Extraction for RAG
|
||||
----------------------------
|
||||
|
||||
To build RAG (Retrieval-Augmented Generation) applications, you can configure prompt targets with parameters,
|
||||
enabling Arch to retrieve critical information in a structured way for processing. This approach improves the
|
||||
retrieval quality and speed of your application. By extracting parameters from the conversation, you can pull
|
||||
the appropriate chunks from a vector database or SQL-like data store to enhance accuracy. With Arch, you can
|
||||
streamline data retrieval and processing to build more efficient and precise RAG applications.
|
||||
|
||||
Step 1: Define prompt targets with parameter definitions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. literalinclude:: /_config/rag-prompt-targets.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:emphasize-lines: 16-36
|
||||
:caption: prompt-config.yaml for parameter extraction for RAG scenarios
|
||||
|
||||
Step 2: Process request parameters in Flask
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once the prompt targets are configured as above, handling those parameters is
|
||||
|
||||
.. literalinclude:: /_include/parameter_handling_flask.py
|
||||
:language: python
|
||||
:linenos:
|
||||
:caption: Flask API example for parameter extraction via HTTP request parameters
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
Technical Architecture
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
intro/terminology
|
||||
intro/threading_model
|
||||
listeners/listeners
|
||||
prompt_processing/prompt_processing
|
||||
listeners/llm_provider
|
||||
model_serving/model_serving
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
.. _arch_terminology:
|
||||
|
||||
Terminology
|
||||
============
|
||||
|
||||
A few definitions before we dive into the main architecture documentation. Arch borrows from Envoy's terminology
|
||||
to keep things consistent in logs, traces and in code.
|
||||
|
||||
**Downstream(Ingress)**: An downstream client (web application, etc.) connects to Arch, sends prompts, and receives responses.
|
||||
|
||||
**Upstream(Egress)**: An upstream host that receives connections and prompts from Arch, and returns context or responses for a prompt
|
||||
|
||||
.. image:: /_static/img/network-topology-ingress-egress.jpg
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
**Listener**: A listener is a named network location (e.g., port, address, path etc.) that Arch listens on to process prompts
|
||||
before forwarding them to your application server endpoints. rch enables you to configure one listener for downstream connections
|
||||
(like port 80, 443) and creates a separate internal listener for calls that initiate from your application code to LLMs.
|
||||
|
||||
.. Note::
|
||||
|
||||
When you start Arch, you specify a listener address/port that you want to bind downstream. But, Arch uses are predefined port
|
||||
that you can use (``127.0.0.1:10000``) to proxy egress calls originating from your application to LLMs (API-based or hosted).
|
||||
For more details, check out :ref:`LLM providers <llm_providers>`
|
||||
|
||||
**Instance**: An instance of the Arch gateway. When you start Arch it creates at most two processes. One to handle Layer 7
|
||||
networking operations (auth, tls, observability, etc) and the second process to serve models that enable it to make smart
|
||||
decisions on how to accept, handle and forward prompts. The second process is optional, as the model serving sevice could be
|
||||
hosted on a different network (an API call). But these two processes are considered a single instance of Arch.
|
||||
|
||||
**Prompt Targets**: Arch offers a primitive called ``prompt_targets`` to help separate business logic from undifferentiated
|
||||
work in building generative AI apps. Prompt targets are endpoints that receive prompts that are processed by Arch.
|
||||
For example, Arch enriches incoming prompts with metadata like knowing when a request is a follow-up or clarifying prompt
|
||||
so that you can build faster, more accurate retrieval (RAG) apps. To support agentic apps, like scheduling travel plans or
|
||||
sharing comments on a document - via prompts, Bolt uses its function calling abilities to extract critical information from
|
||||
the incoming prompt (or a set of prompts) needed by a downstream backend API or function call before calling it directly.
|
||||
|
||||
**Error Targets**: Error targets are those endpoints that receive forwarded errors from Arch when issues arise,
|
||||
such as failing to properly call a function/API, detecting violations of guardrails, or encountering other processing errors.
|
||||
These errors are communicated to the application via headers (X-Arch-[ERROR-TYPE]), allowing it to handle the errors gracefully
|
||||
and take appropriate actions.
|
||||
|
||||
**Model Serving**: Arch is a set of **two** self-contained processes that are designed to run alongside your application servers
|
||||
(or on a separate hostconnected via a network).The **model serving** process helps Arch make intelligent decisions about the
|
||||
incoming prompts. The model server is designed to call the (fast) purpose-built :ref:`LLMs <llms_in_arch>` in Arch.
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
.. _arch_overview_threading:
|
||||
|
||||
Threading model
|
||||
===============
|
||||
|
||||
Arch builds on top of Envoy's single process with multiple threads architecture.
|
||||
|
||||
A single *primary* thread controls various sporadic coordination tasks while some number of *worker*
|
||||
threads perform filtering, and forwarding.
|
||||
|
||||
Once a connection is accepted, the connection spends the rest of its lifetime bound to a single worker
|
||||
thread. All the functionality around prompt handling from a downstream client is handled in a separate worker thread.
|
||||
This allows the majority of Arch to be largely single threaded (embarrassingly parallel) with a small amount
|
||||
of more complex code handling coordination between the worker threads.
|
||||
|
||||
Generally Arch is written to be 100% non-blocking.
|
||||
|
||||
.. tip::
|
||||
|
||||
For most workloads we recommend configuring the number of worker threads to be equal to the number of
|
||||
hardware threads on the machine.
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
.. _arch_overview_listeners:
|
||||
|
||||
Listener
|
||||
---------
|
||||
Listener is a top level primitive in Arch, which simplifies the configuration required to bind incoming
|
||||
connections from downstream clients, and for egress connections to LLMs (hosted or API)
|
||||
|
||||
Arch builds on Envoy's Listener subsystem to streamline connection managemet for developers. Arch minimizes
|
||||
the complexity of Envoy's listener setup by using best-practices and exposing only essential settings,
|
||||
making it easier for developers to bind connections without deep knowledge of Envoy’s configuration model. This
|
||||
simplification ensures that connections are secure, reliable, and optimized for performance.
|
||||
|
||||
Downstream (Ingress)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Developers can configure Arch to accept connections from downstream clients. A downstream listener acts as the
|
||||
primary entry point for incoming traffic, handling initial connection setup, including network filtering, gurdrails,
|
||||
and additional network security checks. For more details on prompt security and safety,
|
||||
see :ref:`here <arch_overview_prompt_handling>`
|
||||
|
||||
Upstream (Egress)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Arch automatically configures a listener to route requests from your application to upstream LLM API providers (or hosts).
|
||||
When you start Arch, it creates a listener for egress traffic based on the presence of the ``llm_providers`` configuration
|
||||
section in the ``prompt_config.yml`` file. Arch binds itself to a local address such as ``127.0.0.1:9000/v1`` or a DNS-based
|
||||
address like ``arch.local:9000/v1`` for outgoing traffic. For more details on LLM providers, read :ref:`here <llm_providers>`
|
||||
|
||||
Configure Listener
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To configure a Downstream (Ingress) Listner, simply add the ``listener`` directive to your ``prompt_config.yml`` file:
|
||||
|
||||
.. literalinclude:: /_config/getting-started.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:lines: 1-18
|
||||
:emphasize-lines: 2-5
|
||||
:caption: :download:`arch-getting-started.yml </_config/getting-started.yml>`
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
.. _llm_providers:
|
||||
|
||||
LLM Provider
|
||||
------------
|
||||
|
||||
``llm_provider`` is a top-level primitive in Arch, helping developers centrally define, secure, observe,
|
||||
and manage the usage of of their LLMs. Arch builds on Envoy's reliable `cluster subsystem <https://www.envoyproxy.io/docs/envoy/v1.31.2/intro/arch_overview/upstream/cluster_manager>`_
|
||||
to manage egress traffic to LLMs, which includes intelligent routing, retry and fail-over mechanisms,
|
||||
ensuring high availability and fault tolerance. This abstraction also enables developers to seamlessly switching between LLM providers or upgrade LLM versions, simplifying the integration and scaling of LLMs across
|
||||
applications.
|
||||
|
||||
|
||||
Below is an example of how you can configure ``llm_providers`` with an instance of an Arch gateway.
|
||||
|
||||
.. literalinclude:: /_config/getting-started.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:lines: 1-20
|
||||
:emphasize-lines: 11-18
|
||||
:caption: :download:`arch-getting-started.yml </_config/getting-started.yml>`
|
||||
|
||||
.. Note::
|
||||
When you start Arch, it creates a listener port for egress traffic based on the presence of ``llm_providers``
|
||||
configuration section in the ``prompt_config.yml`` file. Arch binds itself to a local address such as
|
||||
``127.0.0.1:9000/v1`` or a DNS-based address like ``arch.local:9000/v1`` for egress traffic.
|
||||
|
||||
Arch also offers vendor-agnostic SDKs and libraries to make LLM calls to API-based LLM providers (like OpenAI,
|
||||
Anthropic, Mistral, Cohere, etc.) and supports calls to OSS LLMs that are hosted on your infrastructure. Arch
|
||||
abstracts the complexities of integrating with different LLM providers, providing a unified interface for making
|
||||
calls, handling retries, managing rate limits, and ensuring seamless integration with cloud-based and on-premise
|
||||
LLMs. Simply configure the details of the LLMs your application will use, and Arch offers a unified interface to
|
||||
make outbound LLM calls.
|
||||
|
||||
Example: Using the Arch Python SDK
|
||||
----------------------------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from arch_client import ArchClient
|
||||
|
||||
# Initialize the Arch client
|
||||
client = ArchClient(base_url="http://127.0.0.1:9000/v1")
|
||||
|
||||
# Define your LLM provider and prompt
|
||||
model_id = "openai"
|
||||
prompt = "What is the capital of France?"
|
||||
|
||||
# Send the prompt to the LLM through Arch
|
||||
response = client.completions.create(llm_provider=llm_provider, prompt=prompt)
|
||||
|
||||
# Print the response
|
||||
print("LLM Response:", response)
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
.. _arch_model_serving:
|
||||
|
||||
Model Serving
|
||||
-------------
|
||||
|
||||
Arch is a set of **two** self-contained processes that are designed to run alongside your application
|
||||
servers (or on a separate host connected via a network). The first process is designated to manage low-level
|
||||
networking and HTTP related comcerns, and the other process is for **model serving**, which helps Arch make
|
||||
intelligent decisions about the incoming prompts. The model server is designed to call the purpose-built
|
||||
:ref:`LLMs <llms_in_arch>` in Arch.
|
||||
|
||||
.. image:: /_static/img/arch-system-architecture.jpg
|
||||
:align: center
|
||||
:width: 50%
|
||||
|
||||
_____________________________________________________________________________________________________________
|
||||
|
||||
Arch' is designed to be deployed in your cloud VPC, on a on-premises host, and can work on devices that don't
|
||||
have a GPU. Note, GPU devices are need for fast and cost-efficient use, so that Arch (model server, specifically)
|
||||
can process prompts quickly and forward control back to the applicaton host. There are three modes in which Arch
|
||||
can be configured to run its **model server** subsystem:
|
||||
|
||||
Local Serving (CPU - Moderate)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The following bash commands enable you to configure the model server subsystem in Arch to run local on device
|
||||
and only use CPU devices. This will be the slowest option but can be useful in dev/test scenarios where GPUs
|
||||
might not be available.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
archgw up --local -cpu
|
||||
|
||||
Local Serving (GPU- Fast)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The following bash commands enable you to configure the model server subsystem in Arch to run locally on the
|
||||
machine and utilize the GPU available for fast inference across all model use cases, including function calling
|
||||
guardails, etc.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
archgw up --local
|
||||
|
||||
Cloud Serving (GPU - Blazing Fast)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
The command below instructs Arch to intelligently use GPUs locally for fast intent detection, but default to
|
||||
cloud serving for function calling and guardails scenarios to dramatically improve the speed and overall performance
|
||||
of your applications.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
archgw up
|
||||
|
||||
.. Note::
|
||||
Arch's model serving in the cloud is priced at $0.05M/token (156x cheaper than GPT-4o) with averlage latency
|
||||
of 200ms (10x faster than GPT-4o). Please refer to our :ref:`getting started guide <getting_started>` to know
|
||||
how to generate API keys for model serving
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
.. _arch_overview_prompt_handling:
|
||||
|
||||
Prompts
|
||||
-------
|
||||
|
||||
Arch's primary design point is to securely accept, process and handle prompts. To do that effectively,
|
||||
Arch relies on Envoy's HTTP `connection management <https://www.envoyproxy.io/docs/envoy/v1.31.2/intro/arch_overview/http/http_connection_management>`_,
|
||||
subsystem and its **prompt handler** subsystem engineered with purpose-built :ref:`LLMs <llms_in_arch>` to
|
||||
implement critical functionality on behalf of developers so that you can stay focused on business logic.
|
||||
|
||||
.. Note::
|
||||
Arch's **prompt handler** subsystem interacts with the **model** subsytem through Envoy's cluster manager
|
||||
system to ensure robust, resilient and fault-tolerant experience in managing incoming prompts. Read more
|
||||
about the :ref:`model subsystem <arch_model_serving>` and how the LLMs are hosted in Arch.
|
||||
|
||||
Messages
|
||||
--------
|
||||
|
||||
Arch accepts messages directly from the body of the HTTP request in a format that follows the `Hugging Face Messages API <https://huggingface.co/docs/text-generation-inference/en/messages_api>`_.
|
||||
This design allows developers to pass a list of messages, where each message is represented as a dictionary
|
||||
containing two key-value pairs:
|
||||
|
||||
- **Role**: Defines the role of the message sender, such as "user" or "assistant".
|
||||
- **Content**: Contains the actual text of the message.
|
||||
|
||||
|
||||
Prompt Guardrails
|
||||
-----------------
|
||||
|
||||
Arch is engineered with :ref:`Arch-Guard <llms_in_arch>`, an industry leading safety layer, powered by a
|
||||
compact and high-performimg LLM that monitors incoming prompts to detect and reject jailbreak attempts -
|
||||
ensuring that unauthorized or harmful behaviors are intercepted early in the process.
|
||||
|
||||
To add jailbreak guardrails, see example below:
|
||||
|
||||
.. literalinclude:: /_config/getting-started.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:emphasize-lines: 24-27
|
||||
:caption: :download:`arch-getting-started.yml </_config/getting-started.yml>`
|
||||
|
||||
.. Note::
|
||||
As a roadmap item, Arch will expose the ability for developers to define custom guardrails via Arch-Guard-v2,
|
||||
and add support for additional safety checks defined by developers and hazardous categories like, violent crimes, privacy, hate,
|
||||
etc. To offer feedback on our roadmap, please visit our `github page <https://github.com/orgs/katanemo/projects/1>`_
|
||||
|
||||
|
||||
Prompt Targets
|
||||
--------------
|
||||
|
||||
Once a prompt passes any configured guardrail checks, Arch processes the contents of the incoming conversation
|
||||
and identifies where to forwad the conversation to via its essential ``prompt_targets`` primitve. Prompt targets
|
||||
are endpoints that receive prompts that are processed by Arch. For example, Arch enriches incoming prompts with
|
||||
metadata like knowing when a user's intent has changed so that you can build faster, more accurate RAG apps.
|
||||
|
||||
Configuring ``prompt_targets`` is simple. See example below:
|
||||
|
||||
.. literalinclude:: /_config/getting-started.yml
|
||||
:language: yaml
|
||||
:linenos:
|
||||
:emphasize-lines: 29-38
|
||||
:caption: :download:`arch-getting-started.yml </_config/getting-started.yml>`
|
||||
|
||||
|
||||
Intent Detection and Prompt Matching:
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Arch uses fast Natural Language Inference (NLI) and embedding approaches to first detect the intent of each
|
||||
incoming prompt. This intent detection phase analyzes the prompt's content and matches it against predefined
|
||||
prompt targets, ensuring that each prompt is forwarded to the most appropriate endpoint. Arch’s intent
|
||||
detection framework considers both the name and description of each prompt target, and uses a composite matching
|
||||
score between an NLI and cosine similarity to enchance accuracy in forwarding decisions.
|
||||
|
||||
- **Embeddings**: By embedding the prompt and comparing it to known target vectors, Arch effectively identifies
|
||||
the closest match, ensuring that the prompt is handled by the correct downstream service.
|
||||
|
||||
- **NLI**: NLI techniques further refine the matching process by evaluating the semantic alignment between the
|
||||
prompt and potential targets.
|
||||
|
||||
Agentic Apps via Prompt Targets
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To support agentic apps, like scheduling travel plans or sharing comments on a document - via prompts, Arch uses
|
||||
its function calling abilities to extract critical information from the incoming prompt (or a set of prompts)
|
||||
needed by a downstream backend API or function call before calling it directly. For more details on how you can
|
||||
build agentic applications using Arch, see our full guide :ref:`here <arch_function_calling_agentic_guide>`:
|
||||
|
||||
.. Note::
|
||||
Arch :ref:`Arch-FC <llms_in_arch>` is the dedicated agentic model engineered in Arch to extract information from
|
||||
a (set of) prompts and executes necessary backend API calls. This allows for efficient handling of agentic tasks,
|
||||
such as scheduling data retrieval, by dynamically interacting with backend services. Arch-FC is a flagship 1.3
|
||||
billion parameter model that matches performance with frontier models like Claude Sonnet 3.5 ang GPT-4, while
|
||||
being 100x cheaper ($0.05M/token hosted) and 10x faster (p50 latencies of 200ms).
|
||||
|
||||
Prompting LLMs
|
||||
--------------
|
||||
Arch is a single piece of software that is designed to manage both ingress and egress prompt traffic, drawing its
|
||||
distributed proxy nature from the robust `Envoy <https://envoyproxy.io>`_. This makes it extremely efficient and capable
|
||||
of handling upstream connections to LLMs. If your application is originating code to an API-based LLM, simply use
|
||||
Arch's Python or JavaScript client SDK to send traffic to the desired LLM of choice. By sending traffic through Arch,
|
||||
you can propagate traces, manage and monitor traffic, apply rate limits, and utilize a large set of traffic management
|
||||
capabilities in a central place.
|
||||
|
||||
.. Attention::
|
||||
When you start Arch, it automatically creates a listener port for egress calls to upstream LLMs. This is based on the
|
||||
``llm_providers`` configuration section in the ``prompt_config.yml`` file. Arch binds itself to a local address such as
|
||||
127.0.0.1:9000/v1 or a DNS-based address like arch.local:9000/v1 for outgoing traffic.
|
||||
|
||||
Example: Using the Arch Python SDK
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from arch_client import ArchClient
|
||||
|
||||
# Initialize the Arch client
|
||||
client = ArchClient(base_url="http://127.0.0.1:9000/v1")
|
||||
|
||||
# Define your LLM provider and prompt
|
||||
model_id = "openai"
|
||||
prompt = "What is the capital of France?"
|
||||
|
||||
# Send the prompt to the LLM through Arch
|
||||
response = client.completions.create(llm_provider=llm_provider, prompt=prompt)
|
||||
|
||||
# Print the response
|
||||
print("LLM Response:", response)
|
||||
|
||||
Example: Using OpenAI Client with Arch as an Egress Gateway
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import openai
|
||||
|
||||
# Set the OpenAI API base URL to the Arch gateway endpoint
|
||||
openai.api_base = "http://127.0.0.1:9000/v1"
|
||||
|
||||
# No need to set openai.api_key since it's configured in Arch's gateway
|
||||
|
||||
# Use the OpenAI client as usual
|
||||
response = openai.Completion.create(
|
||||
model="text-davinci-003",
|
||||
prompt="What is the capital of France?"
|
||||
)
|
||||
|
||||
print("OpenAI Response:", response.choices[0].text.strip())
|
||||
|
||||
In these examples:
|
||||
|
||||
The ArchClient is used to send traffic directly through the Arch egress proxy to the LLM of your choice, such as OpenAI.
|
||||
The OpenAI client is configured to route traffic via Arch by setting the proxy to 127.0.0.1:9000, assuming Arch is
|
||||
running locally and bound to that address and port.
|
||||
|
||||
This setup allows you to take advantage of Arch's advanced traffic management features while interacting with LLM APIs like OpenAI.
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
.. _getting_help:
|
||||
|
||||
Getting help
|
||||
============
|
||||
|
||||
We are very interested in building a community around Arch. Please reach out to us if you are
|
||||
interested in using it and need help or want to contribute.
|
||||
|
||||
Please see `contact info <https://github.com/katanemo/arch#contact>`_.
|
||||
|
||||
Reporting security vulnerabilities
|
||||
----------------------------------
|
||||
|
||||
Please see `security contact info
|
||||
<https://github.com/katanemo/arch#reporting-security-vulnerabilities>`_.
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
.. _intro:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
what_is_arch
|
||||
architecture/architecture
|
||||
life_of_a_request
|
||||
getting_help
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
.. _life_of_a_request:
|
||||
|
||||
Life of a Request
|
||||
=================
|
||||
|
||||
Below we describe the events in the life of a request passing through an Arch gateway instance. We first
|
||||
describe how Arch fits into the request path and then the internal events that take place following
|
||||
the arrival of a request at Arch from downtream clients. We follow the request until the corresponding
|
||||
dispatch upstream and the response path.
|
||||
|
||||
.. image:: /_static/img/network-topology-ingress-egress.jpg
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
|
||||
We recommend that you get familiar with some of the :ref:`terminology <arch_terminology>` used in Arch
|
||||
before reading this section.
|
||||
|
||||
Network topology
|
||||
----------------
|
||||
|
||||
How a request flows through the components in a network (including Arch) depends on the network’s topology.
|
||||
Arch can be used in a wide variety of networking topologies. We focus on the inner operation of Arch below,
|
||||
but briefly we address how Arch relates to the rest of the network in this section.
|
||||
|
||||
- **Downstream(Ingress)** listeners take requests from upstream clients like a web UI or clients that forward
|
||||
prompts to you local application responses from the application flow back through Arch to the downstream.
|
||||
|
||||
- **Upstream(Egress)** listeners take requests from the application and forward them to LLMs.
|
||||
|
||||
.. image:: /_static/img/network-topology-ingress-egress.jpg
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
In practice, Arch can be deployed on the edge and as an internal load balancer between AI agents. A request path may
|
||||
traverse multiple Arch gateways:
|
||||
|
||||
.. image:: /_static/img/network-topology-agent.jpg
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
|
||||
High level architecture
|
||||
-----------------------
|
||||
Arch is a set of **two** self-contained processes that are designed to run alongside your application servers
|
||||
(or on a separate server connected to your application servers via a network). The first process is designated
|
||||
to manage HTTP-level networking and connection management concerns (protocol management, request id generation,
|
||||
header sanitization, etc.), and the other process is for **model serving**, which helps Arch make intelligent
|
||||
decisions about the incoming prompts. The model server hosts the purpose-built :ref:`LLMs <llms_in_arch>` to
|
||||
manage several critical, but undifferentiated, prompt related tasks on behalf of developers.
|
||||
|
||||
|
||||
The request processing path in Arch has three main parts:
|
||||
|
||||
* :ref:`Listener subsystem <arch_overview_listeners>` which handles **downstream** and **upstream** request
|
||||
processing. It is responsible for managing the downstream (ingress) and the upstream (egress) request
|
||||
lifecycle. The downstream and upstream HTTP/2 codec lives here.
|
||||
* :ref:`Prompt handler subsystem <arch_overview_prompt_handling>` which is responsible for selecting and
|
||||
forwarding prompts ``prompt_targets`` and establishes the lifecycle of any **upstream** connection to a
|
||||
hosted endpoint that implements domain-specific business logic for incoming promots. This is where knowledge
|
||||
of targets and endpoint health, load balancing and connection pooling exists.
|
||||
* :ref:`Model serving subsystem <arch_model_serving>` which helps Arch make intelligent decisions about the
|
||||
incoming prompts. The model server is designed to call the purpose-built :ref:`LLMs <llms_in_arch>` in Arch.
|
||||
|
||||
The three subsystems are bridged with either the HTTP router filter, and the cluster manager subsystems of Envoy.
|
||||
|
||||
Also, Arch utilizes `Envoy event-based thread model <https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310>`_.
|
||||
A main thread is responsible forthe server lifecycle, configuration processing, stats, etc. and some number of
|
||||
:ref:`worker threads <arch_overview_threading>` process requests. All threads operate around an event loop (`libevent <https://libevent.org/>`_)
|
||||
and any given downstream TCP connection will be handled by exactly one worker thread for its lifetime. Each worker
|
||||
thread maintains its own pool of TCP connections to upstream endpoints.
|
||||
|
||||
Worker threads rarely share state and operate in a trivially parallel fashion. This threading model
|
||||
enables scaling to very high core count CPUs.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Today, only support a static bootstrap configuration file for simplicity today:
|
||||
|
||||
.. literalinclude:: /_config/getting-started.yml
|
||||
:language: yaml
|
||||
|
||||
|
||||
Request Flow (Ingress)
|
||||
----------------------
|
||||
|
||||
Overview
|
||||
^^^^^^^^
|
||||
A brief outline of the life cycle of a request and response using the example configuration above:
|
||||
|
||||
1. **TCP Connection Establishment**:
|
||||
A TCP connection from downstream is accepted by an Arch listener running on a worker thread.
|
||||
The listener filter chain provides SNI and other pre-TLS information. The transport socket, typically TLS,
|
||||
decrypts incoming data for processing.
|
||||
|
||||
2. **Prompt Guardrails Check**:
|
||||
Arch first checks the incoming prompts for guardrails such as jailbreak attempts. This ensures
|
||||
that harmful or unwanted behaviors are detected early in the request processing pipeline.
|
||||
|
||||
3. **Intent Matching**:
|
||||
The decrypted data stream is deframed by the HTTP/2 codec in Arch's HTTP connection manager. Arch performs
|
||||
intent matching via is **prompt-handler** subsystem using the name and description of the defined prompt targets,
|
||||
determining which endpoint should handle the prompt.
|
||||
|
||||
4. **Parameter Gathering with Arch-FC**:
|
||||
If a prompt target requires specific parameters, Arch engages Arch-FC to extract the necessary details
|
||||
from the incoming prompt(s). This process gathers the critical information needed for downstream API calls.
|
||||
|
||||
5. **API Call Execution**:
|
||||
Arch routes the prompt to the appropriate backend API or function call. If an endpoint cluster is identified,
|
||||
load balancing is performed, circuit breakers are checked, and the request is proxied to the upstream endpoint.
|
||||
|
||||
6. **Default Summarization by Upstream LLM**:
|
||||
By default, if no specific endpoint processing is needed, the prompt is sent to an upstream LLM for summarization.
|
||||
This ensures that responses are concise and relevant, enhancing user experience in RAG (Retrieval-Augmented Generation)
|
||||
and agentic applications.
|
||||
|
||||
7. **Error Handling and Forwarding**:
|
||||
Errors encountered during processing, such as failed function calls or guardrail detections, are forwarded to
|
||||
designated error targets. Error details are communicated through specific headers to the application:
|
||||
|
||||
- ``X-Function-Error-Code``: Code indicating the type of function call error.
|
||||
- ``X-Prompt-Guard-Error-Code``: Code specifying violations detected by prompt guardrails.
|
||||
- Additional headers carry messages and timestamps to aid in debugging and logging.
|
||||
|
||||
8. **Response Handling**:
|
||||
The upstream endpoint’s TLS transport socket encrypts the response, which is then proxied back downstream.
|
||||
Responses pass through HTTP filters in reverse order, ensuring any necessary processing or modification before final delivery.
|
||||
|
||||
|
||||
Request Flow (Egress)
|
||||
---------------------
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
A brief outline of the life cycle of a request and response in the context of egress traffic from an application
|
||||
to Large Language Models (LLMs) via Arch:
|
||||
|
||||
1. **HTTP Connection Establishment to LLM**:
|
||||
Arch initiates an HTTP connection to the upstream LLM service. This connection is handled by Arch’s egress listener
|
||||
running on a worker thread. The connection typically uses a secure transport protocol such as HTTPS, ensuring the
|
||||
prompt data is encrypted before being sent to the LLM service.
|
||||
|
||||
2. **Rate Limiting**:
|
||||
Before sending the request to the LLM, Arch applies rate-limiting policies to ensure that the upstream LLM service
|
||||
is not overwhelmed by excessive traffic. Rate limits are enforced per client or service, ensuring fair usage and
|
||||
preventing accidental or malicious overload. If the rate limit is exceeded, Arch may return an appropriate HTTP
|
||||
error (e.g., 429 Too Many Requests) without sending the prompt to the LLM.
|
||||
|
||||
3. **Load Balancing to (hosted) LLM Endpoints**:
|
||||
After passing the rate-limiting checks, Arch routes the prompt to the appropriate LLM endpoint.
|
||||
If multiple LLM providers instances are available, load balancing is performed to distribute traffic evenly
|
||||
across the instances. Arch checks the health of the LLM endpoints using circuit breakers and health checks,
|
||||
ensuring that the prompt is only routed to a healthy, responsive instance.
|
||||
|
||||
4. **Response Reception and Forwarding**:
|
||||
Once the LLM processes the prompt, Arch receives the response from the LLM service. The response is typically a
|
||||
generated text, completion, or summarization. Upon reception, Arch decrypts (if necessary) and handles the response,
|
||||
passing it through any egress processing pipeline defined by the application, such as logging or additional response filtering.
|
||||
|
||||
|
||||
Post-request processing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Once a request completes, the stream is destroyed. The following also takes places:
|
||||
|
||||
* The post-request :ref:`monitoring <monitoring>` are updated (e.g. timing, active requests, upgrades, health checks).
|
||||
Some statistics are updated earlier however, during request processing. Stats are batchedand written by the main
|
||||
thread periodically.
|
||||
* :ref:`Access logs <arch_access_logging>` are written to the access log
|
||||
* :ref:`Trace <arch_overview_tracing>` spans are finalized. If our example request was traced, a
|
||||
trace span, describing the duration and details of the request would be created by the HCM when
|
||||
processing request headers and then finalized by the HCM during post-request processing.
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
What is Arch
|
||||
============
|
||||
|
||||
Arch is an intelligent `(Layer 7) <https://www.cloudflare.com/learning/ddos/what-is-layer-7/>`_ gateway
|
||||
designed for generative AI apps, AI agents, and Co-pilots that work with prompts. Engineered with purpose-built
|
||||
:ref:`LLMs <llms_in_arch>`, Arch handles all the critical but undifferentiated tasks related to the handling and
|
||||
processing of prompts, including detecting and rejecting `jailbreak <https://github.com/verazuo/jailbreak_llms>`_
|
||||
attempts, intelligently calling “backend” APIs to fulfill the user's request represented in a prompt, routing to
|
||||
and offering disaster recovery between upstream LLMs, and managing the observability of prompts and LLM interactions
|
||||
in a centralized way.
|
||||
|
||||
.. image:: /_static/img/arch-logo.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
**The project was born out of the belief that:**
|
||||
|
||||
*Prompts are nuanced and opaque user requests, which require the same capabilities as traditional HTTP requests
|
||||
including secure handling, intelligent routing, robust observability, and integration with backend (API)
|
||||
systems for personalization - all outside business logic.*
|
||||
|
||||
|
||||
In practice, achieving the above goal is incredibly difficult. Arch attempts to do so by providing the
|
||||
following high level features:
|
||||
|
||||
_____________________________________________________________________________________________________________
|
||||
|
||||
**Out-of-process architecture, built on** `Envoy <http://envoyproxy.io/>`_: Arch is takes a dependency on
|
||||
Envoy and is a self-contained process that is designed to run alongside your application servers. Arch uses
|
||||
Envoy's HTTP connection management subsystem, HTTP L7 filtering and telemetry capabilities to extend the
|
||||
functionality exclusively for prompts and LLMs. This gives Arch several advantages:
|
||||
|
||||
* Arch builds on Envoy's proven success. Envoy is used at masssive sacle by the leading technology companies of
|
||||
our time including `AirBnB <https://www.airbnb.com>`_, `Dropbox <https://www.dropbox.com>`_,
|
||||
`Google <https://www.google.com>`_, `Reddit <https://www.reddit.com>`_, `Stripe <https://www.stripe.com>`_,
|
||||
etc. Its battle tested and scales linearly with usage and enables developers to focus on what really matters:
|
||||
application features and business logic.
|
||||
|
||||
* Arch works with any application language. A single Arch deployment can act as gateway for AI applications
|
||||
written in Python, Java, C++, Go, Php, etc.
|
||||
|
||||
* Arch can be deployed and upgraded quickly across your infrastructure transparently without the horrid pain
|
||||
of deploying library upgrades in your applications.
|
||||
|
||||
**Engineered with Fast LLMs:** Arch is engineered with specialized (sub-billion) LLMs that are desgined for
|
||||
fast, cost-effective and acurrate handling of prompts. These :ref:`LLMs <llms_in_arch>` are designed to be
|
||||
best-in-class for critcal prompt-related tasks like:
|
||||
|
||||
* **Function/API Calling:** Arch helps you easily personalize your applications by enabling calls to
|
||||
application-specific (API) operations via user prompts. This involves any predefined functions or APIs
|
||||
you want to expose to users to perform tasks, gather information, or manipulate data. With function calling,
|
||||
you have flexibility to support "agentic" experiences tailored to specific use cases - from updating insurance
|
||||
claims to creating ad campaigns - via prompts. Arch analyzes prompts, extracts critical information from
|
||||
prompts, engages in lightweight conversation to gather any missing parameters and makes API calls so that you can
|
||||
focus on writing business logic. For more details, read :ref:`prompt processing <arch_overview_prompt_handling>`.
|
||||
|
||||
* **Prompt Guardrails:** Arch helps you improve the safety of your application by applying prompt guardrails in
|
||||
a centralized way for better governance hygiene. With prompt guardrails you can prevent `jailbreak <https://github.com/verazuo/jailbreak_llms>`_
|
||||
attempts or toxicity present in user's prompts without having to write a single line of code. To learn more
|
||||
about how to configure guardrails available in Arch, read :ref:`prompt processing <arch_overview_prompt_handling>`.
|
||||
|
||||
* **Intent-Drift Detection:** Developers struggle to handle `follow-up <https://www.reddit.com/r/ChatGPTPromptGenius/comments/17dzmpy/how_to_use_rag_with_conversation_history_for/?>`_,
|
||||
or `clarifying <https://www.reddit.com/r/LocalLLaMA/comments/18mqwg6/best_practice_for_rag_with_followup_chat/>`_
|
||||
questions. Specifically, when users ask for modifications or additions to previous responses their AI applications
|
||||
often generate entirely new responses instead of adjusting the previous ones. Arch offers intent-drift detection as a
|
||||
feature so that developers know when the user has shifted away from the previous intent so that they can improve
|
||||
their retrieval, lower overall token cost and dramatically improve the speed and accuracy of their responses back
|
||||
to users.
|
||||
|
||||
**Traffic Management:** Arch offers several capabilities for LLM calls originating from your applications, including a
|
||||
vendor-agnostic SDK to make LLM calls, smart retries on errors from upstream LLMs, and automatic cutover to other LLMs
|
||||
configured in Arch for continuous availability and disaster recovery scenarios. Arch extends Envoy's `cluster subsystem
|
||||
<https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/cluster_manager>`_ to manage upstream connections
|
||||
to LLMs so that you can build resilient AI applications.
|
||||
|
||||
**Front/edge Gateway:** There is substantial benefit in using the same software at the edge (observability,
|
||||
traffic shaping alogirithms, applying guardrails, etc.) as for outbound LLM inference use cases. Arch has the feature set
|
||||
that makes it exceptionally well suited as an edge gateway for AI applications. This includes TLS termination, rate limiting,
|
||||
and prompt-based routing.
|
||||
|
||||
**Best-In Class Monitoring:** Arch offers several monitoring metrics that help you understand three
|
||||
critical aspects of your application: latency, token usage, and error rates by an upstream LLM provider. Latency
|
||||
measures the speed at which your application is responding to users, which includes metrics like time to first
|
||||
token (TFT), time per output token (TOT) metrics, and the total latency as perceived by users.
|
||||
|
||||
**End-to-End Tracing:** Arch propagates trace context using the W3C Trace Context standard, specifically through
|
||||
the ``traceparent`` header. This allows each component in the system to record its part of the request flow,
|
||||
enabling **end-to-end tracing** across the entire application. By using OpenTelemetry, Arch ensures that
|
||||
developers can capture this trace data consistently and in a format compatible with various observability tools.
|
||||
For more details, read :ref:`tracing <arch_overview_tracing>`.
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
.. _llms_in_arch:
|
||||
|
||||
LLMs
|
||||
====
|
||||
|
||||
Arch utilizes purpose-built, industry leading, LLMs to handle the crufty and undifferentiated work around
|
||||
accepting, handling and processing prompts. The following sections talk about some of the core models that
|
||||
are built-in Arch.
|
||||
|
||||
Arch-Guard-v1
|
||||
-------------
|
||||
LLM-powered applications are susceptible to prompt attacks, which are prompts intentionally designed to
|
||||
subvert the developer’s intended behavior of the LLM. Arch-Guard-v1 is a classifier model trained on a large
|
||||
corpus of attacks, capable of detecting explicitly malicious prompts (and toxicity).
|
||||
|
||||
The model is useful as a starting point for identifying and guardrailing against the most risky realistic
|
||||
inputs to LLM-powered applications. Our goal in embedding Arch-Guard in the Arch gateway is to enable developers
|
||||
to focus on their business logic and factor out security and safety outside application logic. Wth Arch-Guard-v1
|
||||
developers can take to significantly reduce prompt attack risk while maintaining control over the user experience.
|
||||
|
||||
Below is our test results of the strength of our model as compared to Prompt-Guard from `Meta LLama <https://huggingface.co/meta-llama/Prompt-Guard-86M>`_.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 15 15 10 15 15
|
||||
|
||||
* - Dataset
|
||||
- Jailbreak (Yes/No)
|
||||
- Samples
|
||||
- Prompt-Guard Accuracy
|
||||
- Arch-Guard Accuracy
|
||||
* - casual_conversation
|
||||
- 0
|
||||
- 3725
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - commonqa
|
||||
- 0
|
||||
- 9741
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - financeqa
|
||||
- 0
|
||||
- 1585
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - instruction
|
||||
- 0
|
||||
- 5000
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - jailbreak_behavior_benign
|
||||
- 0
|
||||
- 100
|
||||
- 0.10
|
||||
- 0.20
|
||||
* - jailbreak_behavior_harmful
|
||||
- 1
|
||||
- 100
|
||||
- 0.30
|
||||
- 0.52
|
||||
* - jailbreak_judge
|
||||
- 1
|
||||
- 300
|
||||
- 0.33
|
||||
- 0.49
|
||||
* - jailbreak_prompts
|
||||
- 1
|
||||
- 79
|
||||
- 0.99
|
||||
- 1.00
|
||||
* - jailbreak_tweet
|
||||
- 1
|
||||
- 1282
|
||||
- 0.16
|
||||
- 0.35
|
||||
* - jailbreak_v
|
||||
- 1
|
||||
- 20000
|
||||
- 0.90
|
||||
- 0.93
|
||||
* - jailbreak_vigil
|
||||
- 1
|
||||
- 104
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - mental_health
|
||||
- 0
|
||||
- 3512
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - telecom
|
||||
- 0
|
||||
- 4000
|
||||
- 1.00
|
||||
- 1.00
|
||||
* - truthqa
|
||||
- 0
|
||||
- 817
|
||||
- 1.00
|
||||
- 0.98
|
||||
* - weather
|
||||
- 0
|
||||
- 3121
|
||||
- 1.00
|
||||
- 1.00
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 15 20
|
||||
|
||||
* - Statistics
|
||||
- Overall performance
|
||||
* - Overall Accuracy
|
||||
- 0.93568 (Prompt-Guard), 0.95267 (Arch-Guard)
|
||||
* - True positives rate (TPR)
|
||||
- 0.8468 (Prompt-Guard), 0.8887 (Arch-Guard)
|
||||
* - True negative rate (TNR)
|
||||
- 0.9972 (Prompt-Guard), 0.9970 (Arch-Guard)
|
||||
* - False positive rate (FPR)
|
||||
- 0.0028 (Prompt-Guard), 0.0030 (Arch-Guard)
|
||||
* - False negative rate (FNR)
|
||||
- 0.1532 (Prompt-Guard), 0.1113 (Arch-Guard)
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 15 20
|
||||
|
||||
* - Metrics
|
||||
- Values
|
||||
* - AUC
|
||||
- 0.857 (Prompt-Guard), 0.880 (Arch-Guard)
|
||||
* - Precision
|
||||
- 0.715 (Prompt-Guard), 0.761 (Arch-Guard)
|
||||
* - Recall
|
||||
- 0.999 (Prompt-Guard), 0.999 (Arch-Guard)
|
||||
|
||||
|
||||
|
||||
Arch-FC
|
||||
-------
|
||||
Arch-FC is a lean, powerful and cost-effective agentic model designed for function calling scenarios.
|
||||
You can run Arch-FC locally, or use the cloud-hosted version for as little as $0.05/M token (100x cheaper
|
||||
than GPT-4o), with a p50 latency of 200ms (5x faster than GPT-4o), while meeting frontier model performance.
|
||||
|
||||
.. Note::
|
||||
Function calling helps you personalize the GenAI experience by calling application-specific operations via
|
||||
prompts. This involves any predefined functions or APIs you want to expose to perform tasks, gather
|
||||
information, or manipulate data - via prompts.
|
||||
|
||||
You can get started with function calling simply by configuring a prompt target with a name, description
|
||||
and set of parameters needed by a specific backend function or a hosted API. The name, and description helps
|
||||
Arch-FC match a user prompt to a function or API that can process it.
|
||||
|
||||
By using Arch-FC, Arch enables you to easily build agentic workflows tailored to domain-specific use cases -
|
||||
from updating insurance claims to creating ad campaigns. Arch-FC analyzes prompts, extracts critical information
|
||||
from prompts, engages in lightweight conversations with the user to gather any missing parameters need before
|
||||
handling control back to Arch to make the API call to your hosted backend. Arch-FC handles the muck of information
|
||||
extraction so that you can focus on the business logic of your application.
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
.. _arch_access_logging:
|
||||
|
||||
Access Logging
|
||||
==============
|
||||
|
||||
Access logging in Arch refers to the logging of detailed information about each request and response that flows through Arch.
|
||||
It provides visibility into the traffic passing through Arch, which is crucial for monitoring, debugging, and analyzing the
|
||||
behavior of AI applications and their interactions.
|
||||
|
||||
Key Features of Access Logging in Arch:
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* **Per-Request Logging**:
|
||||
Each request that passes through Arch is logged. This includes important metadata such as HTTP method,
|
||||
path, response status code, request duration, upstream host, and more.
|
||||
* **Integration with Monitoring Tools**:
|
||||
Access logs can be exported to centralized logging systems (e.g., ELK stack or Fluentd) or used to feed monitoring and alerting systems.
|
||||
* **Structured Logging**: where each request is logged as a object, making it easier to parse and analyze using tools like Elasticsearch and Kibana.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
[2024-09-27T14:52:01.123Z] "ARCH REQUEST" GET /path/to/resource HTTP/1.1 200 512 1024 56 upstream_service.com D
|
||||
X-Arch-Upstream-Service-Time: 25
|
||||
X-Arch-Attempt-Count: 1
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
.. _observability:
|
||||
|
||||
Observability
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
tracing
|
||||
stats
|
||||
access_logs
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
.. _monitoring:
|
||||
|
||||
Monitoring
|
||||
==========
|
||||
|
||||
Arch offers several monitoring metrics that help you understand three critical aspects of your application:
|
||||
latency, token usage, and error rates by an upstream LLM provider. Latency measures the speed at which your
|
||||
application is responding to users, which includes metrics like time to first token (TFT), time per output
|
||||
token (TOT) metrics, and the total latency as perceived by users.
|
||||
|
|
@ -1,313 +0,0 @@
|
|||
.. _arch_overview_tracing:
|
||||
|
||||
Tracing
|
||||
=======
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
`OpenTelemetry <https://opentelemetry.io/>`_ is an open-source observability framework providing APIs
|
||||
and instrumentation for generating, collecting, processing, and exporting telemetry data, such as traces,
|
||||
metrics, and logs. Its flexible design supports a wide range of backends and seamlessly integrates with
|
||||
modern application tools. A key feature of OpenTelemetry is its commitment to standards like the
|
||||
`W3C Trace Context <https://www.w3.org/TR/trace-context/>`_
|
||||
|
||||
**Tracing** is a critical tool that allows developers to visualize and understand the flow of
|
||||
requests in an AI application. With tracing, you can capture a detailed view of how requests propagate
|
||||
through various services and components, which is crucial for **debugging**, **performance optimization**,
|
||||
and understanding complex AI agent architectures like Co-pilots.
|
||||
|
||||
**Arch** propagates trace context using the W3C Trace Context standard, specifically through the
|
||||
``traceparent`` header. This allows each component in the system to record its part of the request
|
||||
flow, enabling **end-to-end tracing** across the entire application. By using OpenTelemetry, Arch ensures
|
||||
that developers can capture this trace data consistently and in a format compatible with various observability
|
||||
tools.
|
||||
______________________________________________________________________________________________
|
||||
|
||||
Benefits of using ``traceparent`` headers
|
||||
-----------------------------------------
|
||||
|
||||
- **Standardization**: The W3C Trace Context standard ensures compatibility across ecosystem tools, allowing
|
||||
traces to be propagated uniformly through different layers of the system.
|
||||
- **Ease of Integration**: OpenTelemetry's design allows developers to easily integrate tracing with minimal
|
||||
changes to their codebase, enabling quick adoption of end-to-end observability.
|
||||
- **Interoperability**: Works seamlessly with popular tracing tools like AWS X-Ray, Datadog, Jaeger, and many others,
|
||||
making it easy to visualize traces in the tools you're already usi
|
||||
|
||||
How to initiate a trace
|
||||
-----------------------
|
||||
|
||||
1. **Enable Tracing Configuration**: Simply add the ``tracing: 100`` flag to in the :ref:`listener <arch_overview_listeners>` config
|
||||
|
||||
2. **Trace Context Propagation**: Arch automatically propagates the ``traceparent`` header. When a request is received, Arch will:
|
||||
|
||||
- Generate a new ``traceparent`` header if one is not present.
|
||||
- Extract the trace context from the ``traceparent`` header if it exists.
|
||||
- Start a new span representing its processing of the request.
|
||||
- Forward the ``traceparent`` header to downstream services.
|
||||
|
||||
3. **Sampling Policy**: The 100 in ``tracing: 100`` means that all the requests as sampled for tracing.
|
||||
You can adjust this value from 0-100.
|
||||
|
||||
|
||||
Trace Propagation
|
||||
-----------------
|
||||
|
||||
Arch uses the W3C Trace Context standard for trace propagation, which relies on the ``traceparent`` header.
|
||||
This header carries tracing information in a standardized format, enabling interoperability between different
|
||||
tracing systems.
|
||||
|
||||
Header Format
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The ``traceparent`` header has the following format::
|
||||
|
||||
traceparent: {version}-{trace-id}-{parent-id}-{trace-flags}
|
||||
|
||||
- {version}: The version of the Trace Context specification (e.g., ``00``).
|
||||
- {trace-id}: A 16-byte (32-character hexadecimal) unique identifier for the trace.
|
||||
- {parent-id}: An 8-byte (16-character hexadecimal) identifier for the parent span.
|
||||
- {trace-flags}: Flags indicating trace options (e.g., sampling).
|
||||
|
||||
Instrumentation
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
To integrate AI tracing, your application needs to follow a few simple steps. The steps
|
||||
below are very common practice, and not unique to Arch, when you reading tracing headers and export
|
||||
`spans <https://docs.lightstep.com/docs/understand-distributed-tracing>`_ for distributed tracing.
|
||||
|
||||
- Read the ``traceparent`` header from incoming requests.
|
||||
- Start new spans as children of the extracted context.
|
||||
- Include the ``traceparent`` header in outbound requests to propagate trace context.
|
||||
- Send tracing data to a collector or tracing backend to export spans
|
||||
|
||||
Example with OpenTelemetry in Python
|
||||
************************************
|
||||
|
||||
Install OpenTelemetry packages:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
|
||||
pip install opentelemetry-instrumentation-requests
|
||||
|
||||
Set up the tracer and exporter:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
||||
from opentelemetry.instrumentation.requests import RequestsInstrumentor
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from opentelemetry.sdk.trace import TracerProvider
|
||||
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
||||
|
||||
# Define the service name
|
||||
resource = Resource(attributes={
|
||||
"service.name": "customer-support-agent"
|
||||
})
|
||||
|
||||
# Set up the tracer provider and exporter
|
||||
tracer_provider = TracerProvider(resource=resource)
|
||||
otlp_exporter = OTLPSpanExporter(endpoint="otel-collector:4317", insecure=True)
|
||||
span_processor = BatchSpanProcessor(otlp_exporter)
|
||||
tracer_provider.add_span_processor(span_processor)
|
||||
trace.set_tracer_provider(tracer_provider)
|
||||
|
||||
# Instrument HTTP requests
|
||||
RequestsInstrumentor().instrument()
|
||||
|
||||
Handle incoming requests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.propagate import extract, inject
|
||||
import requests
|
||||
|
||||
def handle_request(request):
|
||||
# Extract the trace context
|
||||
context = extract(request.headers)
|
||||
tracer = trace.get_tracer(__name__)
|
||||
|
||||
with tracer.start_as_current_span("process_customer_request", context=context):
|
||||
# Example of processing a customer request
|
||||
print("Processing customer request...")
|
||||
|
||||
# Prepare headers for outgoing request to payment service
|
||||
headers = {}
|
||||
inject(headers)
|
||||
|
||||
# Make outgoing request to external service (e.g., payment gateway)
|
||||
response = requests.get("http://payment-service/api", headers=headers)
|
||||
|
||||
print(f"Payment service response: {response.content}")
|
||||
|
||||
|
||||
AI Agent Tracing Visualization Example
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following is an example of tracing for an AI-powered customer support system.
|
||||
A customer interacts with AI agents, which forward their requests through different
|
||||
specialized services and external systems.
|
||||
|
||||
::
|
||||
|
||||
+--------------------------+
|
||||
| Customer Interaction |
|
||||
+--------------------------+
|
||||
|
|
||||
v
|
||||
+--------------------------+ +--------------------------+
|
||||
| Agent 1 (Main - Arch) | ----> | External Payment Service |
|
||||
+--------------------------+ +--------------------------+
|
||||
| |
|
||||
v v
|
||||
+--------------------------+ +--------------------------+
|
||||
| Agent 2 (Support - Arch)| ----> | Internal Tech Support |
|
||||
+--------------------------+ +--------------------------+
|
||||
| |
|
||||
v v
|
||||
+--------------------------+ +--------------------------+
|
||||
| Agent 3 (Orders- Arch) | ----> | Inventory Management |
|
||||
+--------------------------+ +--------------------------+
|
||||
|
||||
Trace Breakdown:
|
||||
****************
|
||||
|
||||
- Customer Interaction:
|
||||
- Span 1: Customer initiates a request via the AI-powered chatbot for billing support (e.g., asking for payment details).
|
||||
|
||||
- AI Agent 1 (Main - Arch):
|
||||
- Span 2: AI Agent 1 (Main) processes the request and identifies it as related to billing, forwarding the request
|
||||
to an external payment service.
|
||||
- Span 3: AI Agent 1 determines that additional technical support is needed for processing and forwards the request
|
||||
to AI Agent 2.
|
||||
|
||||
- External Payment Service:
|
||||
- Span 4: The external payment service processes the payment-related request (e.g., verifying payment status) and sends
|
||||
the response back to AI Agent 1.
|
||||
|
||||
- AI Agent 2 (Tech - Arch):
|
||||
- Span 5: AI Agent 2, responsible for technical queries, processes a request forwarded from AI Agent 1 (e.g., checking for
|
||||
any account issues).
|
||||
- Span 6: AI Agent 2 forwards the query to Internal Tech Support for further investigation.
|
||||
|
||||
- Internal Tech Support:
|
||||
- Span 7: Internal Tech Support processes the request (e.g., resolving account access issues) and responds to AI Agent 2.
|
||||
|
||||
- AI Agent 3 (Orders - Arch):
|
||||
- Span 8: AI Agent 3 handles order-related queries. AI Agent 1 forwards the request to AI Agent 3 after payment verification
|
||||
is completed.
|
||||
- Span 9: AI Agent 3 forwards a request to the Inventory Management system to confirm product availability for a pending order.
|
||||
|
||||
- Inventory Management:
|
||||
- Span 10: The Inventory Management system checks stock and availability and returns the information to AI Agent 3.
|
||||
|
||||
Integrating with Tracing Tools
|
||||
------------------------------
|
||||
|
||||
AWS X-Ray
|
||||
~~~~~~~~~
|
||||
|
||||
To send tracing data to `AWS X-Ray <https://aws.amazon.com/xray/>`_ :
|
||||
|
||||
1. **Configure OpenTelemetry Collector**: Set up the collector to export traces to AWS X-Ray.
|
||||
|
||||
Collector configuration (``otel-collector-config.yaml``):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
|
||||
processors:
|
||||
batch:
|
||||
|
||||
exporters:
|
||||
awsxray:
|
||||
region: your-aws-region
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [awsxray]
|
||||
|
||||
2. **Deploy the Collector**: Run the collector as a Docker container, Kubernetes pod, or standalone service.
|
||||
3. **Ensure AWS Credentials**: Provide AWS credentials to the collector, preferably via IAM roles.
|
||||
4. **Verify Traces**: Access the AWS X-Ray console to view your traces.
|
||||
|
||||
Datadog
|
||||
~~~~~~~
|
||||
|
||||
Datadog
|
||||
|
||||
To send tracing data to `Datadog <https://docs.datadoghq.com/getting_started/tracing/>`_:
|
||||
|
||||
1. **Configure OpenTelemetry Collector**: Set up the collector to export traces to Datadog.
|
||||
|
||||
Collector configuration (``otel-collector-config.yaml``):
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
|
||||
processors:
|
||||
batch:
|
||||
|
||||
exporters:
|
||||
datadog:
|
||||
api:
|
||||
key: "${DD_API_KEY}"
|
||||
site: "${DD_SITE}"
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [datadog]
|
||||
|
||||
2. **Set Environment Variables**: Provide your Datadog API key and site.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
export DD_API_KEY=your_datadog_api_key
|
||||
export DD_SITE=datadoghq.com # Or datadoghq.eu
|
||||
|
||||
3. **Deploy the Collector**: Run the collector in your environment.
|
||||
4. **Verify Traces**: Access the Datadog APM dashboard to view your traces.
|
||||
|
||||
|
||||
Best Practices
|
||||
--------------
|
||||
|
||||
- **Consistent Instrumentation**: Ensure all services propagate the ``traceparent`` header.
|
||||
- **Secure Configuration**: Protect sensitive data and secure communication between services.
|
||||
- **Performance Monitoring**: Be mindful of the performance impact and adjust sampling rates accordingly.
|
||||
- **Error Handling**: Implement proper error handling to prevent tracing issues from affecting your application.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
By leveraging the ``traceparent`` header for trace context propagation, Arch enables developers to implement
|
||||
tracing efficiently. This approach simplifies the process of collecting and analyzing tracing data in common
|
||||
tools like AWS X-Ray and Datadog, enhancing observability and facilitating faster debugging and optimization.
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
- **OpenTelemetry Documentation**: https://opentelemetry.io/docs/
|
||||
- **W3C Trace Context Specification**: https://www.w3.org/TR/trace-context/
|
||||
- **AWS X-Ray Exporter**: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/awsxrayexporter
|
||||
- **Datadog Exporter**: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/datadogexporter
|
||||
|
||||
.. Note::
|
||||
Replace placeholders like ``your-aws-region``, and ``DD_API_KEY`` with your actual configurations.
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
Documentation
|
||||
=============
|
||||
|
||||
.. image:: /_static/img/arch-logo.png
|
||||
:width: 100%
|
||||
:align: center
|
||||
|
||||
**Arch is built on (and by the core contributors of) Envoy proxy with the belief that:**
|
||||
|
||||
*Prompts are nuanced and opaque user requests, which require the same capabilities as traditional HTTP requests
|
||||
including secure handling, intelligent routing, robust observability, and integration with backend (API)
|
||||
systems for personalization - all outside business logic.*
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
intro/intro
|
||||
getting_started/getting_started
|
||||
getting_started/use_cases
|
||||
observability/observability
|
||||
llms/llms
|
||||
configuration_reference
|
||||
101
_sphinx_design_static/design-tabs.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// @ts-check
|
||||
|
||||
// Extra JS capability for selected tabs to be synced
|
||||
// The selection is stored in local storage so that it persists across page loads.
|
||||
|
||||
/**
|
||||
* @type {Record<string, HTMLElement[]>}
|
||||
*/
|
||||
let sd_id_to_elements = {};
|
||||
const storageKeyPrefix = "sphinx-design-tab-id-";
|
||||
|
||||
/**
|
||||
* Create a key for a tab element.
|
||||
* @param {HTMLElement} el - The tab element.
|
||||
* @returns {[string, string, string] | null} - The key.
|
||||
*
|
||||
*/
|
||||
function create_key(el) {
|
||||
let syncId = el.getAttribute("data-sync-id");
|
||||
let syncGroup = el.getAttribute("data-sync-group");
|
||||
if (!syncId || !syncGroup) return null;
|
||||
return [syncGroup, syncId, syncGroup + "--" + syncId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the tab selection.
|
||||
*
|
||||
*/
|
||||
function ready() {
|
||||
// Find all tabs with sync data
|
||||
|
||||
/** @type {string[]} */
|
||||
let groups = [];
|
||||
|
||||
document.querySelectorAll(".sd-tab-label").forEach((label) => {
|
||||
if (label instanceof HTMLElement) {
|
||||
let data = create_key(label);
|
||||
if (data) {
|
||||
let [group, id, key] = data;
|
||||
|
||||
// add click event listener
|
||||
// @ts-ignore
|
||||
label.onclick = onSDLabelClick;
|
||||
|
||||
// store map of key to elements
|
||||
if (!sd_id_to_elements[key]) {
|
||||
sd_id_to_elements[key] = [];
|
||||
}
|
||||
sd_id_to_elements[key].push(label);
|
||||
|
||||
if (groups.indexOf(group) === -1) {
|
||||
groups.push(group);
|
||||
// Check if a specific tab has been selected via URL parameter
|
||||
const tabParam = new URLSearchParams(window.location.search).get(
|
||||
group
|
||||
);
|
||||
if (tabParam) {
|
||||
console.log(
|
||||
"sphinx-design: Selecting tab id for group '" +
|
||||
group +
|
||||
"' from URL parameter: " +
|
||||
tabParam
|
||||
);
|
||||
window.sessionStorage.setItem(storageKeyPrefix + group, tabParam);
|
||||
}
|
||||
}
|
||||
|
||||
// Check is a specific tab has been selected previously
|
||||
let previousId = window.sessionStorage.getItem(
|
||||
storageKeyPrefix + group
|
||||
);
|
||||
if (previousId === id) {
|
||||
// console.log(
|
||||
// "sphinx-design: Selecting tab from session storage: " + id
|
||||
// );
|
||||
// @ts-ignore
|
||||
label.previousElementSibling.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate other tabs with the same sync id.
|
||||
*
|
||||
* @this {HTMLElement} - The element that was clicked.
|
||||
*/
|
||||
function onSDLabelClick() {
|
||||
let data = create_key(this);
|
||||
if (!data) return;
|
||||
let [group, id, key] = data;
|
||||
for (const label of sd_id_to_elements[key]) {
|
||||
if (label === this) continue;
|
||||
// @ts-ignore
|
||||
label.previousElementSibling.checked = true;
|
||||
}
|
||||
window.sessionStorage.setItem(storageKeyPrefix + group, id);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", ready, false);
|
||||
1
_sphinx_design_static/sphinx-design.min.css
vendored
Normal file
BIN
_static/0fecf1cc5677455886b4.woff2
Normal file
BIN
_static/1d5fc702ab9000c3247a.woff
Normal file
BIN
_static/2fe080a3bf49bdc12fcb.woff2
Normal file
BIN
_static/39bd78ffb50669d6855a.woff
Normal file
BIN
_static/4f183a25813446a47ad9.woff2
Normal file
BIN
_static/5f68b8c26e28d783a591.woff2
Normal file
BIN
_static/63a0f5d460fb58135365.woff
Normal file
BIN
_static/70e1dc5f5622381d6e9e.woff
Normal file
BIN
_static/84504970850f0632d9c3.woff
Normal file
BIN
_static/a1e4997bd1fb9d7822e1.woff2
Normal file
BIN
_static/a61d04152f1635036f0d.woff2
Normal file
|
Before Width: | Height: | Size: 289 KiB |
1
_static/awesome-docsearch.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
:root{--docsearch-primary-color:hsl(var(--primary));--docsearch-muted-color:hsl(var(--muted-foreground));--docsearch-key-gradient:transparent;--docsearch-key-shadow:transparent;--docsearch-text-color:hsl(var(--popover-foreground));--docsearch-modal-width:760px;--docsearch-modal-background:hsl(var(--popover));--docsearch-footer-background:hsl(var(--popover));--docsearch-searchbox-focus-background:hsl(var(--popover));--docsearch-container-background:hsl(var(--background)/0.8);--docsearch-spacing:0.5rem;--docsearch-hit-active-color:hsl(var(--accent-foreground));--docsearch-hit-background:transparent;--docsearch-searchbox-shadow:none;--docsearch-hit-shadow:none;--docsearch-modal-shadow:none;--docsearch-footer-shadow:none}.DocSearch-Button{background-color:transparent;border-color:hsl(var(--input));border-radius:.5em;border-style:solid;border-width:1px;display:flex;font-size:.875rem;line-height:1.25rem;width:90%;--tw-ring-offset-color:hsl(var(--background));transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.DocSearch-Button:hover{--tw-shadow:0 0 transparent;--tw-shadow-colored:0 0 transparent;box-shadow:0 0 transparent,0 0 transparent,0 0 transparent;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)}.DocSearch-Button:focus,.DocSearch-Button:hover{background-color:hsl(var(--accent));color:hsl(var(--accent-foreground))}.DocSearch-Button:focus-visible{outline:2px solid transparent;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),0 0 transparent;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 transparent);--tw-ring-color:hsl(var(--ring));--tw-ring-offset-width:2px}.DocSearch-Button-Placeholder{display:block;font-size:.875rem;font-weight:500;line-height:1.25rem}.DocSearch-Button-Key{background-color:hsl(var(--muted));border-color:hsl(var(--border));border-radius:.25rem;border-style:solid;border-width:1px;color:hsl(var(--muted-foreground));font-size:12px}.DocSearch-Container{position:fixed;--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:blur(4px) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:blur(4px) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.DocSearch-Modal{border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px}.DocSearch-SearchBar{border-bottom-left-radius:0;border-bottom-right-radius:0;border-bottom-width:1px;border-color:hsl(var(--input));border-top-left-radius:var(--radius);border-top-right-radius:var(--radius);padding:0}.DocSearch-Form{border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.DocSearch-Cancel{color:hsl(var(--muted-foreground));font-size:.875rem;line-height:1.25rem;padding-left:.5rem;padding-right:.5rem}.DocSearch-MagnifierLabel,.DocSearch-Search-Icon{stroke-width:2;opacity:.5}.DocSearch-Hit-source{color:hsl(var(--muted-foreground))}.DocSearch-Hit,.DocSearch-Hit a{border-radius:calc(var(--radius) - 4px)}.DocSearch-Hit a:focus-visible{outline-offset:-2px}.DocSearch-Hit[aria-selected=true] a{background-color:hsl(var(--accent));color:hsl(var(--accent-foreground))}.DocSearch-Commands{display:none}.DocSearch-Footer{border-color:hsl(var(--border));border-top-width:1px}
|
||||
0
_static/awesome-docsearch.js
Normal file
1
_static/awesome-sphinx-design.css
Normal file
|
|
@ -0,0 +1 @@
|
|||
:root{--sd-color-tabs-label-active:hsl(var(--foreground));--sd-color-tabs-underline-active:hsl(var(--accent-foreground));--sd-color-tabs-label-hover:hsl(var(--accent-foreground));--sd-color-tabs-overline:hsl(var(--border));--sd-color-tabs-underline:hsl(var(--border))}.sd-card{background-color:hsl(var(--card));border-color:hsl(var(--border));border-radius:var(--radius);border-width:1px;color:hsl(var(--card-foreground));margin-top:1.5rem}.sd-container-fluid{margin-bottom:1.5rem;margin-top:1.5rem}.sd-card-title{font-weight:600!important}.sd-summary-title{color:hsl(var(--muted-foreground));font-weight:500!important}.sd-card-footer,.sd-card-header{font-size:.875rem;line-height:1.25rem}.sd-tab-set{margin-top:1.5rem}.sd-tab-content>p{margin-bottom:1.5rem}.sd-tab-content pre:first-of-type{margin-top:0}.sd-tab-set>label{font-weight:500;letter-spacing:.05em}details.sd-dropdown,details.sd-dropdown:not([open])>.sd-card-header{border-color:hsl(var(--border))}details.sd-dropdown summary:focus{outline-style:solid}.sd-cards-carousel{overflow-x:auto}.sd-shadow-sm{--tw-shadow:0 0 transparent!important;--tw-shadow-colored:0 0 transparent!important;box-shadow:0 0 transparent,0 0 transparent,0 0 transparent!important;box-shadow:var(--tw-ring-offset-shadow,0 0 transparent),var(--tw-ring-shadow,0 0 transparent),var(--tw-shadow)!important}
|
||||
0
_static/awesome-sphinx-design.js
Normal file
|
|
@ -55,7 +55,7 @@ div.sphinxsidebarwrapper {
|
|||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 270px;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
word-wrap: break-word;
|
||||
|
|
|
|||
BIN
_static/c226d7283d0d52c2d32c.woff
Normal file
|
|
@ -1,4 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M5 12l5 5l10 -10" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 313 B |
7
_static/clipboard.min.js
vendored
|
|
@ -1,5 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="2" />
|
||||
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 411 B |
|
|
@ -1,94 +0,0 @@
|
|||
/* Copy buttons */
|
||||
button.copybtn {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
top: .3em;
|
||||
right: .3em;
|
||||
width: 1.7em;
|
||||
height: 1.7em;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s, border .3s, background-color .3s;
|
||||
user-select: none;
|
||||
padding: 0;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 0.4em;
|
||||
/* The colors that GitHub uses */
|
||||
border: #1b1f2426 1px solid;
|
||||
background-color: #f6f8fa;
|
||||
color: #57606a;
|
||||
}
|
||||
|
||||
button.copybtn.success {
|
||||
border-color: #22863a;
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
button.copybtn svg {
|
||||
stroke: currentColor;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Show the copybutton */
|
||||
.highlight:hover button.copybtn, button.copybtn.success {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.highlight button.copybtn:hover {
|
||||
background-color: rgb(235, 235, 235);
|
||||
}
|
||||
|
||||
.highlight button.copybtn:active {
|
||||
background-color: rgb(187, 187, 187);
|
||||
}
|
||||
|
||||
/**
|
||||
* A minimal CSS-only tooltip copied from:
|
||||
* https://codepen.io/mildrenben/pen/rVBrpK
|
||||
*
|
||||
* To use, write HTML like the following:
|
||||
*
|
||||
* <p class="o-tooltip--left" data-tooltip="Hey">Short</p>
|
||||
*/
|
||||
.o-tooltip--left {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.o-tooltip--left:after {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
content: attr(data-tooltip);
|
||||
padding: .2em;
|
||||
font-size: .8em;
|
||||
left: -.2em;
|
||||
background: grey;
|
||||
color: white;
|
||||
white-space: nowrap;
|
||||
z-index: 2;
|
||||
border-radius: 2px;
|
||||
transform: translateX(-102%) translateY(0);
|
||||
transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1);
|
||||
}
|
||||
|
||||
.o-tooltip--left:hover:after {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(-100%) translateY(0);
|
||||
transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1);
|
||||
transition-delay: .5s;
|
||||
}
|
||||
|
||||
/* By default the copy button shouldn't show up when printing a page */
|
||||
@media print {
|
||||
button.copybtn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,248 +0,0 @@
|
|||
// Localization support
|
||||
const messages = {
|
||||
'en': {
|
||||
'copy': 'Copy',
|
||||
'copy_to_clipboard': 'Copy to clipboard',
|
||||
'copy_success': 'Copied!',
|
||||
'copy_failure': 'Failed to copy',
|
||||
},
|
||||
'es' : {
|
||||
'copy': 'Copiar',
|
||||
'copy_to_clipboard': 'Copiar al portapapeles',
|
||||
'copy_success': '¡Copiado!',
|
||||
'copy_failure': 'Error al copiar',
|
||||
},
|
||||
'de' : {
|
||||
'copy': 'Kopieren',
|
||||
'copy_to_clipboard': 'In die Zwischenablage kopieren',
|
||||
'copy_success': 'Kopiert!',
|
||||
'copy_failure': 'Fehler beim Kopieren',
|
||||
},
|
||||
'fr' : {
|
||||
'copy': 'Copier',
|
||||
'copy_to_clipboard': 'Copier dans le presse-papier',
|
||||
'copy_success': 'Copié !',
|
||||
'copy_failure': 'Échec de la copie',
|
||||
},
|
||||
'ru': {
|
||||
'copy': 'Скопировать',
|
||||
'copy_to_clipboard': 'Скопировать в буфер',
|
||||
'copy_success': 'Скопировано!',
|
||||
'copy_failure': 'Не удалось скопировать',
|
||||
},
|
||||
'zh-CN': {
|
||||
'copy': '复制',
|
||||
'copy_to_clipboard': '复制到剪贴板',
|
||||
'copy_success': '复制成功!',
|
||||
'copy_failure': '复制失败',
|
||||
},
|
||||
'it' : {
|
||||
'copy': 'Copiare',
|
||||
'copy_to_clipboard': 'Copiato negli appunti',
|
||||
'copy_success': 'Copiato!',
|
||||
'copy_failure': 'Errore durante la copia',
|
||||
}
|
||||
}
|
||||
|
||||
let locale = 'en'
|
||||
if( document.documentElement.lang !== undefined
|
||||
&& messages[document.documentElement.lang] !== undefined ) {
|
||||
locale = document.documentElement.lang
|
||||
}
|
||||
|
||||
let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT;
|
||||
if (doc_url_root == '#') {
|
||||
doc_url_root = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* SVG files for our copy buttons
|
||||
*/
|
||||
let iconCheck = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-check" width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="#22863a" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<title>${messages[locale]['copy_success']}</title>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M5 12l5 5l10 -10" />
|
||||
</svg>`
|
||||
|
||||
// If the user specified their own SVG use that, otherwise use the default
|
||||
let iconCopy = ``;
|
||||
if (!iconCopy) {
|
||||
iconCopy = `<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-copy" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="#000000" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<title>${messages[locale]['copy_to_clipboard']}</title>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<rect x="8" y="8" width="12" height="12" rx="2" />
|
||||
<path d="M16 8v-2a2 2 0 0 0 -2 -2h-8a2 2 0 0 0 -2 2v8a2 2 0 0 0 2 2h2" />
|
||||
</svg>`
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up copy/paste for code blocks
|
||||
*/
|
||||
|
||||
const runWhenDOMLoaded = cb => {
|
||||
if (document.readyState != 'loading') {
|
||||
cb()
|
||||
} else if (document.addEventListener) {
|
||||
document.addEventListener('DOMContentLoaded', cb)
|
||||
} else {
|
||||
document.attachEvent('onreadystatechange', function() {
|
||||
if (document.readyState == 'complete') cb()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const codeCellId = index => `codecell${index}`
|
||||
|
||||
// Clears selected text since ClipboardJS will select the text when copying
|
||||
const clearSelection = () => {
|
||||
if (window.getSelection) {
|
||||
window.getSelection().removeAllRanges()
|
||||
} else if (document.selection) {
|
||||
document.selection.empty()
|
||||
}
|
||||
}
|
||||
|
||||
// Changes tooltip text for a moment, then changes it back
|
||||
// We want the timeout of our `success` class to be a bit shorter than the
|
||||
// tooltip and icon change, so that we can hide the icon before changing back.
|
||||
var timeoutIcon = 2000;
|
||||
var timeoutSuccessClass = 1500;
|
||||
|
||||
const temporarilyChangeTooltip = (el, oldText, newText) => {
|
||||
el.setAttribute('data-tooltip', newText)
|
||||
el.classList.add('success')
|
||||
// Remove success a little bit sooner than we change the tooltip
|
||||
// So that we can use CSS to hide the copybutton first
|
||||
setTimeout(() => el.classList.remove('success'), timeoutSuccessClass)
|
||||
setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon)
|
||||
}
|
||||
|
||||
// Changes the copy button icon for two seconds, then changes it back
|
||||
const temporarilyChangeIcon = (el) => {
|
||||
el.innerHTML = iconCheck;
|
||||
setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon)
|
||||
}
|
||||
|
||||
const addCopyButtonToCodeCells = () => {
|
||||
// If ClipboardJS hasn't loaded, wait a bit and try again. This
|
||||
// happens because we load ClipboardJS asynchronously.
|
||||
if (window.ClipboardJS === undefined) {
|
||||
setTimeout(addCopyButtonToCodeCells, 250)
|
||||
return
|
||||
}
|
||||
|
||||
// Add copybuttons to all of our code cells
|
||||
const COPYBUTTON_SELECTOR = 'div.highlight pre';
|
||||
const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR)
|
||||
codeCells.forEach((codeCell, index) => {
|
||||
const id = codeCellId(index)
|
||||
codeCell.setAttribute('id', id)
|
||||
|
||||
const clipboardButton = id =>
|
||||
`<button class="copybtn o-tooltip--left" data-tooltip="${messages[locale]['copy']}" data-clipboard-target="#${id}">
|
||||
${iconCopy}
|
||||
</button>`
|
||||
codeCell.insertAdjacentHTML('afterend', clipboardButton(id))
|
||||
})
|
||||
|
||||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes excluded text from a Node.
|
||||
*
|
||||
* @param {Node} target Node to filter.
|
||||
* @param {string} exclude CSS selector of nodes to exclude.
|
||||
* @returns {DOMString} Text from `target` with text removed.
|
||||
*/
|
||||
function filterText(target, exclude) {
|
||||
const clone = target.cloneNode(true); // clone as to not modify the live DOM
|
||||
if (exclude) {
|
||||
// remove excluded nodes
|
||||
clone.querySelectorAll(exclude).forEach(node => node.remove());
|
||||
}
|
||||
return clone.innerText;
|
||||
}
|
||||
|
||||
// Callback when a copy button is clicked. Will be passed the node that was clicked
|
||||
// should then grab the text and replace pieces of text that shouldn't be used in output
|
||||
function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
|
||||
var regexp;
|
||||
var match;
|
||||
|
||||
// Do we check for line continuation characters and "HERE-documents"?
|
||||
var useLineCont = !!lineContinuationChar
|
||||
var useHereDoc = !!hereDocDelim
|
||||
|
||||
// create regexp to capture prompt and remaining line
|
||||
if (isRegexp) {
|
||||
regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)')
|
||||
} else {
|
||||
regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)')
|
||||
}
|
||||
|
||||
const outputLines = [];
|
||||
var promptFound = false;
|
||||
var gotLineCont = false;
|
||||
var gotHereDoc = false;
|
||||
const lineGotPrompt = [];
|
||||
for (const line of textContent.split('\n')) {
|
||||
match = line.match(regexp)
|
||||
if (match || gotLineCont || gotHereDoc) {
|
||||
promptFound = regexp.test(line)
|
||||
lineGotPrompt.push(promptFound)
|
||||
if (removePrompts && promptFound) {
|
||||
outputLines.push(match[2])
|
||||
} else {
|
||||
outputLines.push(line)
|
||||
}
|
||||
gotLineCont = line.endsWith(lineContinuationChar) & useLineCont
|
||||
if (line.includes(hereDocDelim) & useHereDoc)
|
||||
gotHereDoc = !gotHereDoc
|
||||
} else if (!onlyCopyPromptLines) {
|
||||
outputLines.push(line)
|
||||
} else if (copyEmptyLines && line.trim() === '') {
|
||||
outputLines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// If no lines with the prompt were found then just use original lines
|
||||
if (lineGotPrompt.some(v => v === true)) {
|
||||
textContent = outputLines.join('\n');
|
||||
}
|
||||
|
||||
// Remove a trailing newline to avoid auto-running when pasting
|
||||
if (textContent.endsWith("\n")) {
|
||||
textContent = textContent.slice(0, -1)
|
||||
}
|
||||
return textContent
|
||||
}
|
||||
|
||||
|
||||
var copyTargetText = (trigger) => {
|
||||
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
|
||||
|
||||
// get filtered text
|
||||
let exclude = '.linenos';
|
||||
|
||||
let text = filterText(target, exclude);
|
||||
return formatCopyText(text, '', false, true, true, true, '', '')
|
||||
}
|
||||
|
||||
// Initialize with a callback so we can modify the text before copy
|
||||
const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText})
|
||||
|
||||
// Update UI with error/success messages
|
||||
clipboard.on('success', event => {
|
||||
clearSelection()
|
||||
temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success'])
|
||||
temporarilyChangeIcon(event.trigger)
|
||||
})
|
||||
|
||||
clipboard.on('error', event => {
|
||||
temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure'])
|
||||
})
|
||||
}
|
||||
|
||||
runWhenDOMLoaded(addCopyButtonToCodeCells)
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
function escapeRegExp(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes excluded text from a Node.
|
||||
*
|
||||
* @param {Node} target Node to filter.
|
||||
* @param {string} exclude CSS selector of nodes to exclude.
|
||||
* @returns {DOMString} Text from `target` with text removed.
|
||||
*/
|
||||
export function filterText(target, exclude) {
|
||||
const clone = target.cloneNode(true); // clone as to not modify the live DOM
|
||||
if (exclude) {
|
||||
// remove excluded nodes
|
||||
clone.querySelectorAll(exclude).forEach(node => node.remove());
|
||||
}
|
||||
return clone.innerText;
|
||||
}
|
||||
|
||||
// Callback when a copy button is clicked. Will be passed the node that was clicked
|
||||
// should then grab the text and replace pieces of text that shouldn't be used in output
|
||||
export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") {
|
||||
var regexp;
|
||||
var match;
|
||||
|
||||
// Do we check for line continuation characters and "HERE-documents"?
|
||||
var useLineCont = !!lineContinuationChar
|
||||
var useHereDoc = !!hereDocDelim
|
||||
|
||||
// create regexp to capture prompt and remaining line
|
||||
if (isRegexp) {
|
||||
regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)')
|
||||
} else {
|
||||
regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)')
|
||||
}
|
||||
|
||||
const outputLines = [];
|
||||
var promptFound = false;
|
||||
var gotLineCont = false;
|
||||
var gotHereDoc = false;
|
||||
const lineGotPrompt = [];
|
||||
for (const line of textContent.split('\n')) {
|
||||
match = line.match(regexp)
|
||||
if (match || gotLineCont || gotHereDoc) {
|
||||
promptFound = regexp.test(line)
|
||||
lineGotPrompt.push(promptFound)
|
||||
if (removePrompts && promptFound) {
|
||||
outputLines.push(match[2])
|
||||
} else {
|
||||
outputLines.push(line)
|
||||
}
|
||||
gotLineCont = line.endsWith(lineContinuationChar) & useLineCont
|
||||
if (line.includes(hereDocDelim) & useHereDoc)
|
||||
gotHereDoc = !gotHereDoc
|
||||
} else if (!onlyCopyPromptLines) {
|
||||
outputLines.push(line)
|
||||
} else if (copyEmptyLines && line.trim() === '') {
|
||||
outputLines.push(line)
|
||||
}
|
||||
}
|
||||
|
||||
// If no lines with the prompt were found then just use original lines
|
||||
if (lineGotPrompt.some(v => v === true)) {
|
||||
textContent = outputLines.join('\n');
|
||||
}
|
||||
|
||||
// Remove a trailing newline to avoid auto-running when pasting
|
||||
if (textContent.endsWith("\n")) {
|
||||
textContent = textContent.slice(0, -1)
|
||||
}
|
||||
return textContent
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
@import url("theme.css");
|
||||
|
||||
body {
|
||||
font-size: 1em;
|
||||
}
|
||||
101
_static/design-tabs.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// @ts-check
|
||||
|
||||
// Extra JS capability for selected tabs to be synced
|
||||
// The selection is stored in local storage so that it persists across page loads.
|
||||
|
||||
/**
|
||||
* @type {Record<string, HTMLElement[]>}
|
||||
*/
|
||||
let sd_id_to_elements = {};
|
||||
const storageKeyPrefix = "sphinx-design-tab-id-";
|
||||
|
||||
/**
|
||||
* Create a key for a tab element.
|
||||
* @param {HTMLElement} el - The tab element.
|
||||
* @returns {[string, string, string] | null} - The key.
|
||||
*
|
||||
*/
|
||||
function create_key(el) {
|
||||
let syncId = el.getAttribute("data-sync-id");
|
||||
let syncGroup = el.getAttribute("data-sync-group");
|
||||
if (!syncId || !syncGroup) return null;
|
||||
return [syncGroup, syncId, syncGroup + "--" + syncId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the tab selection.
|
||||
*
|
||||
*/
|
||||
function ready() {
|
||||
// Find all tabs with sync data
|
||||
|
||||
/** @type {string[]} */
|
||||
let groups = [];
|
||||
|
||||
document.querySelectorAll(".sd-tab-label").forEach((label) => {
|
||||
if (label instanceof HTMLElement) {
|
||||
let data = create_key(label);
|
||||
if (data) {
|
||||
let [group, id, key] = data;
|
||||
|
||||
// add click event listener
|
||||
// @ts-ignore
|
||||
label.onclick = onSDLabelClick;
|
||||
|
||||
// store map of key to elements
|
||||
if (!sd_id_to_elements[key]) {
|
||||
sd_id_to_elements[key] = [];
|
||||
}
|
||||
sd_id_to_elements[key].push(label);
|
||||
|
||||
if (groups.indexOf(group) === -1) {
|
||||
groups.push(group);
|
||||
// Check if a specific tab has been selected via URL parameter
|
||||
const tabParam = new URLSearchParams(window.location.search).get(
|
||||
group
|
||||
);
|
||||
if (tabParam) {
|
||||
console.log(
|
||||
"sphinx-design: Selecting tab id for group '" +
|
||||
group +
|
||||
"' from URL parameter: " +
|
||||
tabParam
|
||||
);
|
||||
window.sessionStorage.setItem(storageKeyPrefix + group, tabParam);
|
||||
}
|
||||
}
|
||||
|
||||
// Check is a specific tab has been selected previously
|
||||
let previousId = window.sessionStorage.getItem(
|
||||
storageKeyPrefix + group
|
||||
);
|
||||
if (previousId === id) {
|
||||
// console.log(
|
||||
// "sphinx-design: Selecting tab from session storage: " + id
|
||||
// );
|
||||
// @ts-ignore
|
||||
label.previousElementSibling.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate other tabs with the same sync id.
|
||||
*
|
||||
* @this {HTMLElement} - The element that was clicked.
|
||||
*/
|
||||
function onSDLabelClick() {
|
||||
let data = create_key(this);
|
||||
if (!data) return;
|
||||
let [group, id, key] = data;
|
||||
for (const label of sd_id_to_elements[key]) {
|
||||
if (label === this) continue;
|
||||
// @ts-ignore
|
||||
label.previousElementSibling.checked = true;
|
||||
}
|
||||
window.sessionStorage.setItem(storageKeyPrefix + group, id);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", ready, false);
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
const DOCUMENTATION_OPTIONS = {
|
||||
VERSION: '0.1-beta',
|
||||
VERSION: ' v0.1',
|
||||
LANGUAGE: 'en',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
FILE_SUFFIX: '.html',
|
||||
LINK_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '',
|
||||
HAS_SOURCE: false,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false,
|
||||
SHOW_SEARCH_SUMMARY: true,
|
||||
ENABLE_SEARCH_SHORTCUTS: true,
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 44.4 44.4" style="enable-background:new 0 0 44.4 44.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#F5A252;stroke-width:5;stroke-miterlimit:10;}
|
||||
.st1{fill:none;stroke:#579ACA;stroke-width:5;stroke-miterlimit:10;}
|
||||
.st2{fill:none;stroke:#E66581;stroke-width:5;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<title>logo</title>
|
||||
<g>
|
||||
<path class="st0" d="M33.9,6.4c3.6,3.9,3.4,9.9-0.5,13.5s-9.9,3.4-13.5-0.5s-3.4-9.9,0.5-13.5l0,0C24.2,2.4,30.2,2.6,33.9,6.4z"/>
|
||||
<path class="st1" d="M35.1,27.3c2.6,4.6,1.1,10.4-3.5,13c-4.6,2.6-10.4,1.1-13-3.5s-1.1-10.4,3.5-13l0,0
|
||||
C26.6,21.2,32.4,22.7,35.1,27.3z"/>
|
||||
<path class="st2" d="M25.9,17.8c2.6,4.6,1.1,10.4-3.5,13s-10.4,1.1-13-3.5s-1.1-10.4,3.5-13l0,0C17.5,11.7,23.3,13.2,25.9,17.8z"/>
|
||||
<path class="st1" d="M19.2,26.4c3.1-4.3,9.1-5.2,13.3-2.1c1.1,0.8,2,1.8,2.7,3"/>
|
||||
<path class="st0" d="M19.9,19.4c-3.6-3.9-3.4-9.9,0.5-13.5s9.9-3.4,13.5,0.5"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0 128h52.512l29.539-11.077-11.077-43.487-34.051 3.693L0 128Z" fill="#0076D4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M52.513 128s16.6-8.759 19.673-24.277c3.072-15.517-12.091-26.594-35.263-26.594 0-.41 20.343-28.718 20.343-28.718l49.4 1.435L95.71 107.7l-20.452 15.978L52.513 128Z" fill="#002868"/><path fill-rule="evenodd" clip-rule="evenodd" d="M0 60.718 41.025.001s1.006.01 3.282 0c16.082-.068 81.23 3.12 81.23 60.368 0 65.352-73.025 67.631-73.025 67.631s30.495-5.839 30.495-34.816c0-28.978-27.541-32.466-45.264-32.466H0Z" fill="#00A9FF"/></svg>
|
||||
|
Before Width: | Height: | Size: 681 B |
|
|
@ -1 +0,0 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="38.73" height="50" viewBox="0 0 38.73 50"><defs><style>.cls-1{fill:#767677;}.cls-2{fill:#f37726;}.cls-3{fill:#9e9e9e;}.cls-4{fill:#616262;}.cls-5{font-size:17.07px;fill:#fff;font-family:Roboto-Regular, Roboto;}</style></defs><title>logo_jupyterhub</title><g id="Canvas"><path id="path7_fill" data-name="path7 fill" class="cls-1" d="M39.51,3.53a3,3,0,0,1-1.7,2.9A3,3,0,0,1,34.48,6a3,3,0,0,1-.82-3.26,3,3,0,0,1,1.05-1.41A3,3,0,0,1,37.52.86a2.88,2.88,0,0,1,1,.6,3,3,0,0,1,.7.93,3.18,3.18,0,0,1,.28,1.14Z" transform="translate(-1.87 -0.69)"/><path id="path8_fill" data-name="path8 fill" class="cls-2" d="M21.91,38.39c-8,0-15.06-2.87-18.7-7.12a19.93,19.93,0,0,0,37.39,0C37,35.52,30,38.39,21.91,38.39Z" transform="translate(-1.87 -0.69)"/><path id="path9_fill" data-name="path9 fill" class="cls-2" d="M21.91,10.78c8,0,15.05,2.87,18.69,7.12a19.93,19.93,0,0,0-37.39,0C6.85,13.64,13.86,10.78,21.91,10.78Z" transform="translate(-1.87 -0.69)"/><path id="path10_fill" data-name="path10 fill" class="cls-3" d="M10.88,46.66a3.86,3.86,0,0,1-.52,2.15,3.81,3.81,0,0,1-1.62,1.51,3.93,3.93,0,0,1-2.19.34,3.79,3.79,0,0,1-2-.94,3.73,3.73,0,0,1-1.14-1.9,3.79,3.79,0,0,1,.1-2.21,3.86,3.86,0,0,1,1.33-1.78,3.92,3.92,0,0,1,3.54-.53,3.85,3.85,0,0,1,2.14,1.93,3.74,3.74,0,0,1,.37,1.43Z" transform="translate(-1.87 -0.69)"/><path id="path11_fill" data-name="path11 fill" class="cls-4" d="M4.12,9.81A2.18,2.18,0,0,1,2.9,9.48a2.23,2.23,0,0,1-.84-1A2.26,2.26,0,0,1,1.9,7.26a2.13,2.13,0,0,1,.56-1.13,2.18,2.18,0,0,1,2.36-.56,2.13,2.13,0,0,1,1,.76,2.18,2.18,0,0,1,.42,1.2A2.22,2.22,0,0,1,4.12,9.81Z" transform="translate(-1.87 -0.69)"/></g><text class="cls-5" transform="translate(5.24 30.01)">Hub</text></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 297 KiB |
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "طباعة إلى PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "موضوع بواسطة"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "تنزيل ملف المصدر"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "قضية مفتوحة"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "محتويات"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "الصفحة السابقة"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "تنزيل ملف دفتر الملاحظات"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "حقوق النشر"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "قم بتنزيل هذه الصفحة"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "مستودع المصدر"
|
||||
|
||||
msgid "By"
|
||||
msgstr "بواسطة"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "مخزن"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "آخر تحديث في"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "تبديل التنقل"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "موضوع كتاب أبو الهول"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "أقترح تحرير"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "افتح قضية"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "إطلاق"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "وضع ملء الشاشة"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "قم بتحرير هذه الصفحة"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "بواسطة"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "الصفحة التالية"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bg\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Печат в PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Тема от"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Изтеглете изходния файл"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "отворен брой"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Съдържание"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "предишна страница"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Изтеглете файла на бележника"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Авторско право"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Изтеглете тази страница"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Хранилище на източника"
|
||||
|
||||
msgid "By"
|
||||
msgstr "От"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "хранилище"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Последна актуализация на"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Превключване на навигацията"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Тема на книгата Sphinx"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "предложи редактиране"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Отворете проблем"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Стартиране"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Режим на цял екран"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Редактирайте тази страница"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "По"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "Следваща страница"
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bn\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "পিডিএফ প্রিন্ট করুন"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "থিম দ্বারা"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "উত্স ফাইল ডাউনলোড করুন"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "খোলা সমস্যা"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "আগের পৃষ্ঠা"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "নোটবুক ফাইল ডাউনলোড করুন"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "কপিরাইট"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "এই পৃষ্ঠাটি ডাউনলোড করুন"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "উত্স সংগ্রহস্থল"
|
||||
|
||||
msgid "By"
|
||||
msgstr "দ্বারা"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "সর্বশেষ আপডেট"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "নেভিগেশন টগল করুন"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "স্পিনিক্স বুক থিম"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "একটি সমস্যা খুলুন"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "শুরু করা"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "এই পৃষ্ঠাটি সম্পাদনা করুন"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "দ্বারা"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "পরবর্তী পৃষ্ঠা"
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ca\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Imprimeix a PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Tema del"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Baixeu el fitxer font"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "número obert"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "Pàgina anterior"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Descarregar fitxer de quadern"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Copyright"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Descarregueu aquesta pàgina"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Dipòsit de fonts"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Per"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Darrera actualització el"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Commuta la navegació"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Tema del llibre Esfinx"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "suggerir edició"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Obriu un número"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Llançament"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Editeu aquesta pàgina"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Per la"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "pàgina següent"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cs\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Tisk do PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Téma od"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Stáhněte si zdrojový soubor"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "otevřené číslo"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Obsah"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "předchozí stránka"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Stáhnout soubor poznámkového bloku"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "autorská práva"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Stáhněte si tuto stránku"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Zdrojové úložiště"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Podle"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "úložiště"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Naposledy aktualizováno"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Přepnout navigaci"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Téma knihy Sfinga"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "navrhnout úpravy"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Otevřete problém"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Zahájení"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Režim celé obrazovky"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Upravit tuto stránku"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Podle"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "další strana"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: da\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Udskriv til PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Tema af"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Download kildefil"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "åbent nummer"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Indhold"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "forrige side"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Download notesbog-fil"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "ophavsret"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Download denne side"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Kildelager"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Ved"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "lager"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Sidst opdateret den"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Skift navigation"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Sphinx bogtema"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "foreslå redigering"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Åbn et problem"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Start"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Fuldskærmstilstand"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Rediger denne side"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Ved"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "Næste side"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "In PDF drucken"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Thema von der"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Quelldatei herunterladen"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "offenes Thema"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Inhalt"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "vorherige Seite"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Notebook-Datei herunterladen"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Urheberrechte ©"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Laden Sie diese Seite herunter"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Quell-Repository"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Durch"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "Repository"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Zuletzt aktualisiert am"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Navigation umschalten"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Sphinx-Buch-Thema"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "vorschlagen zu bearbeiten"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Öffnen Sie ein Problem"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Starten"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Vollbildmodus"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Bearbeite diese Seite"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Bis zum"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "Nächste Seite"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: el\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Εκτύπωση σε PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Θέμα από το"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Λήψη αρχείου προέλευσης"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "ανοιχτό ζήτημα"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Περιεχόμενα"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "προηγούμενη σελίδα"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Λήψη αρχείου σημειωματάριου"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Πνευματική ιδιοκτησία"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Λήψη αυτής της σελίδας"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Αποθήκη πηγής"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Με"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "αποθήκη"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Τελευταία ενημέρωση στις"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Εναλλαγή πλοήγησης"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Θέμα βιβλίου Sphinx"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "προτείνω επεξεργασία"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Ανοίξτε ένα ζήτημα"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Εκτόξευση"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "ΛΕΙΤΟΥΡΓΙΑ ΠΛΗΡΟΥΣ ΟΘΟΝΗΣ"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Επεξεργαστείτε αυτήν τη σελίδα"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Από το"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "επόμενη σελίδα"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: eo\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Presi al PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Temo de la"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Elŝutu fontodosieron"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "malferma numero"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Enhavo"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "antaŭa paĝo"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Elŝutu kajeran dosieron"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Kopirajto"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Elŝutu ĉi tiun paĝon"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Fonto-deponejo"
|
||||
|
||||
msgid "By"
|
||||
msgstr "De"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "deponejo"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Laste ĝisdatigita la"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Ŝalti navigadon"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Sfinksa Libro-Temo"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "sugesti redaktadon"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Malfermu numeron"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Lanĉo"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Plenekrana reĝimo"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Redaktu ĉi tiun paĝon"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Per la"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "sekva paĝo"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Imprimir en PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Tema por el"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Descargar archivo fuente"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "Tema abierto"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Contenido"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "pagina anterior"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Descargar archivo de cuaderno"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Derechos de autor"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Descarga esta pagina"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Repositorio de origen"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Por"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "repositorio"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Ultima actualización en"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Navegación de palanca"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Tema del libro de la esfinge"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "sugerir editar"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Abrir un problema"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Lanzamiento"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Modo de pantalla completa"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Edita esta página"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Por el"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "siguiente página"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: et\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Prindi PDF-i"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Teema"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Laadige alla lähtefail"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "avatud küsimus"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Sisu"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "eelmine leht"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Laadige sülearvuti fail alla"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Autoriõigus"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Laadige see leht alla"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Allikahoidla"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Kõrval"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "hoidla"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Viimati uuendatud"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Lülita navigeerimine sisse"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Sfinksiraamatu teema"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "soovita muuta"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Avage probleem"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Käivitage"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Täisekraanirežiim"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Muutke seda lehte"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Autor"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "järgmine leht"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fi\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Tulosta PDF-tiedostoon"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Teeman tekijä"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Lataa lähdetiedosto"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "avoin ongelma"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Sisällys"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "Edellinen sivu"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Lataa muistikirjatiedosto"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Tekijänoikeus"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Lataa tämä sivu"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Lähteen arkisto"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Tekijä"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "arkisto"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Viimeksi päivitetty"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Vaihda navigointia"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Sphinx-kirjan teema"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "ehdottaa muokkausta"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Avaa ongelma"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Tuoda markkinoille"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Koko näytön tila"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Muokkaa tätä sivua"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Mukaan"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "seuraava sivu"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Imprimer au format PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Thème par le"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Télécharger le fichier source"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "signaler un problème"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Contenu"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "page précédente"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Télécharger le fichier notebook"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "droits d'auteur"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Téléchargez cette page"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Dépôt source"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Par"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "dépôt"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Dernière mise à jour le"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Basculer la navigation"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Thème du livre Sphinx"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "suggestion de modification"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Ouvrez un problème"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "lancement"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Mode plein écran"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Modifier cette page"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Par le"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "page suivante"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: hr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Ispis u PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Tema autora"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Preuzmi izvornu datoteku"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "otvoreno izdanje"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Sadržaj"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "Prethodna stranica"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Preuzmi datoteku bilježnice"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Autorska prava"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Preuzmite ovu stranicu"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Izvorno spremište"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Po"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "spremište"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Posljednje ažuriranje:"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Uključi / isključi navigaciju"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Tema knjige Sphinx"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "predloži uređivanje"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Otvorite izdanje"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Pokrenite"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Način preko cijelog zaslona"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Uredite ovu stranicu"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Od strane"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "sljedeća stranica"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: id\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Cetak ke PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Tema oleh"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Unduh file sumber"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "masalah terbuka"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Isi"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "halaman sebelumnya"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Unduh file notebook"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "hak cipta"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Unduh halaman ini"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Repositori sumber"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Oleh"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "gudang"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Terakhir diperbarui saat"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Alihkan navigasi"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Tema Buku Sphinx"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "menyarankan edit"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Buka masalah"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Meluncurkan"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Mode layar penuh"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Edit halaman ini"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Oleh"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "halaman selanjutnya"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: it\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "Stampa in PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "Tema di"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "Scarica il file sorgente"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "questione aperta"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "Contenuti"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "pagina precedente"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "Scarica il file del taccuino"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Diritto d'autore"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "Scarica questa pagina"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "Repository di origine"
|
||||
|
||||
msgid "By"
|
||||
msgstr "Di"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "repository"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "Ultimo aggiornamento il"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "Attiva / disattiva la navigazione"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "Tema del libro della Sfinge"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "suggerisci modifica"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "Apri un problema"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "Lanciare"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "Modalità schermo intero"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "Modifica questa pagina"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "Dal"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "pagina successiva"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: iw\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "הדפס לקובץ PDF"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "נושא מאת"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "הורד את קובץ המקור"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "בעיה פתוחה"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "תוכן"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "עמוד קודם"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "הורד קובץ מחברת"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "זכויות יוצרים"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "הורד דף זה"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "מאגר המקורות"
|
||||
|
||||
msgid "By"
|
||||
msgstr "על ידי"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "מאגר"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "עודכן לאחרונה ב"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "החלף ניווט"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "נושא ספר ספינקס"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "מציע לערוך"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "פתח גיליון"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "לְהַשִׁיק"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "מצב מסך מלא"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "ערוך דף זה"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "דרך"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "עמוד הבא"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ja\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "PDFに印刷"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "のテーマ"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "ソースファイルをダウンロード"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "未解決の問題"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "目次"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "前のページ"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "ノートブックファイルをダウンロード"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "Copyright"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "このページをダウンロード"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "ソースリポジトリ"
|
||||
|
||||
msgid "By"
|
||||
msgstr "著者"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "リポジトリ"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "最終更新日"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "ナビゲーションを切り替え"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "スフィンクスの本のテーマ"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "編集を提案する"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "問題を報告"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "起動"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "全画面モード"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "このページを編集"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "によって"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "次のページ"
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sphinx-Book-Theme\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ko\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Print to PDF"
|
||||
msgstr "PDF로 인쇄"
|
||||
|
||||
msgid "Theme by the"
|
||||
msgstr "테마별"
|
||||
|
||||
msgid "Download source file"
|
||||
msgstr "소스 파일 다운로드"
|
||||
|
||||
msgid "open issue"
|
||||
msgstr "열린 문제"
|
||||
|
||||
msgid "Contents"
|
||||
msgstr "내용"
|
||||
|
||||
msgid "previous page"
|
||||
msgstr "이전 페이지"
|
||||
|
||||
msgid "Download notebook file"
|
||||
msgstr "노트북 파일 다운로드"
|
||||
|
||||
msgid "Copyright"
|
||||
msgstr "저작권"
|
||||
|
||||
msgid "Download this page"
|
||||
msgstr "이 페이지 다운로드"
|
||||
|
||||
msgid "Source repository"
|
||||
msgstr "소스 저장소"
|
||||
|
||||
msgid "By"
|
||||
msgstr "으로"
|
||||
|
||||
msgid "repository"
|
||||
msgstr "저장소"
|
||||
|
||||
msgid "Last updated on"
|
||||
msgstr "마지막 업데이트"
|
||||
|
||||
msgid "Toggle navigation"
|
||||
msgstr "탐색 전환"
|
||||
|
||||
msgid "Sphinx Book Theme"
|
||||
msgstr "스핑크스 도서 테마"
|
||||
|
||||
msgid "suggest edit"
|
||||
msgstr "편집 제안"
|
||||
|
||||
msgid "Open an issue"
|
||||
msgstr "이슈 열기"
|
||||
|
||||
msgid "Launch"
|
||||
msgstr "시작하다"
|
||||
|
||||
msgid "Fullscreen mode"
|
||||
msgstr "전체 화면으로보기"
|
||||
|
||||
msgid "Edit this page"
|
||||
msgstr "이 페이지 편집"
|
||||
|
||||
msgid "By the"
|
||||
msgstr "에 의해"
|
||||
|
||||
msgid "next page"
|
||||
msgstr "다음 페이지"
|
||||