diff --git a/docs/api/cody.mdx b/docs/api/cody.mdx
new file mode 100644
index 000000000..5e41fd7df
--- /dev/null
+++ b/docs/api/cody.mdx
@@ -0,0 +1,13 @@
+# Sourcegraph Cody API
+
+## `POST /.api/cody/context`
+
+
+## `POST /.api/llm/chat/completions`
+
+
+## `GET /.api/llm/models`
+
+
+## `GET /.api/llm/models/{modelId}`
+
diff --git a/docs/api/index.mdx b/docs/api/index.mdx
index 92f195b56..d567c14b1 100644
--- a/docs/api/index.mdx
+++ b/docs/api/index.mdx
@@ -3,4 +3,5 @@
Sourcegraph exposes the following APIs:
- [Sourcegraph GraphQL API](/api/graphql/), for accessing data stored or computed by Sourcegraph
+- [Sourcegraph Cody API](/api/cody/), for interacting with Cody.
- [Sourcegraph Stream API](/api/stream_api/), for consuming search results as a stream of events
diff --git a/package.json b/package.json
index f7dd6b1f8..7c3de2f14 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "openapi:watch": "./scripts/openapi-watch.sh"
},
"browserslist": "defaults, not ie <= 11",
"dependencies": {
@@ -46,6 +47,7 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-highlight-words": "^0.20.0",
+ "react-markdown": "^9.0.1",
"react-syntax-highlighter": "^15.5.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-pretty-code": "^0.10.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8cffacec7..37ad8f84b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -113,6 +113,9 @@ dependencies:
react-highlight-words:
specifier: ^0.20.0
version: 0.20.0(react@18.3.1)
+ react-markdown:
+ specifier: ^9.0.1
+ version: 9.0.1(@types/react@18.2.20)(react@18.3.1)
react-syntax-highlighter:
specifier: ^15.5.0
version: 15.5.0(react@18.3.1)
@@ -4192,6 +4195,10 @@ packages:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
dev: false
+ /html-url-attributes@3.0.1:
+ resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
+ dev: false
+
/html-void-elements@2.0.1:
resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==}
dev: false
@@ -6505,6 +6512,28 @@ packages:
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ /react-markdown@9.0.1(@types/react@18.2.20)(react@18.3.1):
+ resolution: {integrity: sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==}
+ peerDependencies:
+ '@types/react': '>=18'
+ react: '>=18'
+ dependencies:
+ '@types/hast': 3.0.3
+ '@types/react': 18.2.20
+ devlop: 1.1.0
+ hast-util-to-jsx-runtime: 2.3.1
+ html-url-attributes: 3.0.1
+ mdast-util-to-hast: 13.2.0
+ react: 18.3.1
+ remark-parse: 11.0.0
+ remark-rehype: 11.1.1
+ unified: 11.0.4
+ unist-util-visit: 5.0.0
+ vfile: 6.0.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/react-remove-scroll-bar@2.3.6(@types/react@18.2.20)(react@18.3.1):
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
engines: {node: '>=10'}
diff --git a/scripts/openapi-compile.sh b/scripts/openapi-compile.sh
new file mode 100755
index 000000000..1623abf8c
--- /dev/null
+++ b/scripts/openapi-compile.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -eux
+
+# Check if yq is installed, if not, install it using Homebrew
+if ! command -v yq &> /dev/null
+then
+ echo "yq could not be found, installing via Homebrew..."
+ if ! command -v brew &> /dev/null
+ then
+ echo "Homebrew is not installed. Please install Homebrew first."
+ exit 1
+ fi
+ brew install yq
+fi
+
+
+SOURCEGRAPH_DIR=$1
+DOCS_DIR=$2
+cd "$SOURCEGRAPH_DIR"
+pnpm -C internal/openapi compile
+yq eval -o=json internal/openapi/tsp-output/@typespec/openapi3/openapi.Sourcegraph.Latest.yaml > "$DOCS_DIR"/src/components/openapi/openapi.Sourcegraph.Latest.json
diff --git a/scripts/openapi-watch.sh b/scripts/openapi-watch.sh
new file mode 100755
index 000000000..646dbb0f5
--- /dev/null
+++ b/scripts/openapi-watch.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+set -eux
+
+
+# Check if entr is installed
+if ! command -v entr &> /dev/null
+then
+ echo "entr could not be found, installing with Homebrew..."
+ brew install entr
+fi
+
+COMPILE_SCRIPT=$(dirname "${BASH_SOURCE[0]}")/openapi-compile.sh
+DOCS_DIR=$(realpath $(dirname "${BASH_SOURCE[0]}")/../)
+SOURCEGRAPH_DIR=${SOURCEGRAPH_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../sourcegraph}
+SOURCEGRAPH_DIR=$(realpath $SOURCEGRAPH_DIR)
+echo $SOURCEGRAPH_DIR/internal/openapi/public.tsp | entr $COMPILE_SCRIPT $SOURCEGRAPH_DIR $DOCS_DIR
diff --git a/src/components/MdxComponents.tsx b/src/components/MdxComponents.tsx
index 09f5b146d..4a5175d1a 100644
--- a/src/components/MdxComponents.tsx
+++ b/src/components/MdxComponents.tsx
@@ -1,6 +1,7 @@
import AWSOneClickLaunchForm from './AWSOneClickLaunchForm';
import { ContentTab, ContentTabs } from './ContentTabs';
import FeatureParity from './FeatureParity';
+import { PreCode, PreCodeBlock } from './PreCodeBlock';
import Accordion from './mdx/Accordion';
import { Callout } from './mdx/Callout';
import { CustomLink } from './mdx/CustomLink';
@@ -9,12 +10,13 @@ import { LinkCard, LinkCards } from './mdx/LinkCards';
import { ProductCard, ProductCards } from './mdx/ProductCards';
import { QuickLink, QuickLinks } from './mdx/QuickLinks';
import { Tab, Tabs } from './mdx/Tabs';
-import { PreCodeBlock, PreCode } from './PreCodeBlock';
+import { ApiOperation } from './openapi/ApiOperation';
import ResourceEstimator from './resource-estimator/ResourceEstimator';
import { Badge } from './ui/badge';
const MdxComponents = (version?: string) => {
return {
+ ApiOperation,
FeatureParity,
ResourceEstimator,
AWSOneClickLaunchForm,
diff --git a/src/components/mdx/Tabs.tsx b/src/components/mdx/Tabs.tsx
index 5e990363c..862af6e14 100644
--- a/src/components/mdx/Tabs.tsx
+++ b/src/components/mdx/Tabs.tsx
@@ -68,6 +68,7 @@ export function Tabs({children}: TabsProps) {
}
interface TabProps {
+ title?: string;
children: ReactNode;
}
diff --git a/src/components/openapi/ApiOperation.tsx b/src/components/openapi/ApiOperation.tsx
new file mode 100644
index 000000000..8f8e6d6c0
--- /dev/null
+++ b/src/components/openapi/ApiOperation.tsx
@@ -0,0 +1,326 @@
+import openapi from './openapi.Sourcegraph.Latest.json';
+
+import {OAISchema, OAISpec, Operation, SchemaProperty} from './types';
+import {ApiSpec, newApiSpec} from './ApiSpec';
+import {Tab, Tabs} from '../mdx/Tabs';
+import {PreCode, PreCodeBlock} from '../PreCodeBlock';
+import Markdown from 'react-markdown'
+
+let spec: ApiSpec | undefined;
+function loadSpec() {
+ if (!spec) {
+ spec = newApiSpec(openapi);
+ }
+ return spec;
+}
+
+export function ApiOperation(props: {operation: string}) {
+ const operation = loadSpec().findOperation(props.operation);
+ if (!operation) {
+ return
Operation {props.operation} not found
;
+ }
+
+ return (
+
+
{operation.description}
+ {example(operation)}
+ {request(operation)}
+ {response(operation)}
+
+ );
+}
+
+function request(operation: Operation) {
+ if (
+ !operation.schema.requestBody &&
+ operation.schema.parameters.length === 0
+ ) {
+ return null;
+ }
+ return (
+
+
Request: application/json
+ {operation.schema.requestBody &&
+ schema(operation.schema.requestBody)}
+ {/* {operation.schema.requestBody && (
+
+ Request body:{' '}
+ {JSON.stringify(operation.schema.requestBody, null, 2)}
+
+ )} */}
+ {/* {operation.schema.parameters && (
+
+ Parameters:{' '}
+ {JSON.stringify(operation.schema.parameters, null, 2)}
+
+ )} */}
+
+ );
+}
+
+function response(operation: Operation) {
+ if (!operation.schema.response) {
+ return null;
+ }
+ return (
+
+
Response: application/json
+ {schema(operation.schema.response)}
+
+ );
+}
+
+function Code(props: {lang: string; children: React.ReactNode}) {
+ return (
+
+ {props.children}
+
+ );
+}
+
+function example(operation: Operation) {
+ if (!operation.example) {
+ return null;
+ }
+ return (
+
+
Example
+
+
+ {curlCommand(operation)}
+
+
+
+ {typescriptExample(operation)}
+
+
+
+ {pythonExample(operation)}
+
+
+
+ Example response:
+
+
+ {JSON.stringify(operation.example.response, null, 2)}
+
+
+ );
+}
+
+function formatJson(json: any, indent: string) {
+ return JSON.stringify(json, null, 2).replaceAll('\n', '\n' + indent);
+}
+
+function typescriptExample(operation: Operation): string {
+ const out: string[] = [];
+ out.push(`import fetch from 'node-fetch'`);
+ out.push(`const endpoint = process.env.SRC_ENDPOINT`);
+ out.push(`const accessToken = process.env.SRC_ACCESS_TOKEN`);
+ out.push(
+ `const response = await fetch(\`\${endpoint}${operation.path}\`, {`
+ );
+ if (operation.method !== 'get') {
+ out.push(` method: '${operation.method}',`);
+ }
+ out.push(` headers: {`);
+ out.push(` 'Authorization': \`token \${accessToken}\`,`);
+ out.push(` },`);
+ if (operation.example?.request) {
+ out.push(
+ ` body: JSON.stringify(${formatJson(
+ operation.example.request,
+ ' '
+ )})`
+ );
+ }
+ out.push(`})`);
+ out.push(`console.log(await response.json())`);
+ return out.join('\n');
+}
+
+function pythonExample(operation: Operation): string {
+ const out: string[] = [];
+ out.push(`import os`);
+ out.push(`import requests`);
+ out.push(`endpoint = os.getenv('SRC_ENDPOINT')`);
+ out.push(`access_token = os.getenv('SRC_ACCESS_TOKEN')`);
+ out.push(`response = requests.${operation.method.toLowerCase()}(`);
+ out.push(` url=f'{endpoint}${operation.path}',`);
+ out.push(` headers={"Authorization": f'token {access_token}'},`);
+ if (operation.example?.request) {
+ out.push(` json=${formatJson(operation.example.request, ' ')}`);
+ }
+ out.push(`)`);
+ out.push(`print(response.json())`);
+ return out.join('\n');
+}
+
+function curlCommand(operation: Operation): string {
+ const out: string[] = [];
+ out.push(`curl "$SRC_ENDPOINT${operation.path}"`);
+ out.push('--header "Authorization: token $SRC_ACCESS_TOKEN"');
+ if (operation.method !== 'get') {
+ out.push(`--request ${operation.method.toUpperCase()}`);
+ }
+ if (operation.example?.request) {
+ out.push(`--data '${formatJson(operation.example.request, ' ')}'`);
+ }
+
+ return out.join(' \\\n ');
+}
+
+function schema(schema: OAISchema): React.ReactNode {
+ const spec = loadSpec();
+ schema = spec.canonical(schema);
+ const rows: React.ReactNode[] = [];
+ const refs = new Set();
+ const isRendered = new Set();
+ schemaProperties(spec, '', schema, rows, refs);
+ while (refs.size > 0) {
+ const ref = refs.values().next().value;
+ refs.delete(ref);
+ if (isRendered.has(ref)) {
+ // Important: avoid infinite loop
+ continue;
+ }
+ isRendered.add(ref);
+ schemaProperties(spec, componentName(ref), spec.ref(ref), rows, refs);
+ }
+
+ return (
+
+
+
+ | Field |
+ Type |
+ Required |
+ Description |
+
+
+ {rows}
+
+ );
+}
+
+function schemaProperties(
+ spec: ApiSpec,
+ prefix: string,
+ schema: OAISchema,
+ rows: React.ReactNode[],
+ refs: Set
+): void {
+ const properties = spec.properties(schema);
+ for (const property of properties) {
+ rows.push(
+
+
+
+ {prefix ? `${prefix}.${property.name}` : property.name}
+
+ |
+ {schemaType(property.schema, refs)} |
+ {schemaRequired(schema, property)} |
+ {schemaDescription(property.schema)} |
+
+ );
+ }
+}
+
+function componentName(component: string): string {
+ return component.split('/').pop() ?? component;
+}
+function refType(ref: string, refs: Set): string {
+ refs.add(ref);
+ return componentName(ref);
+}
+function schemaType(schema: OAISchema, refs: Set): React.ReactNode {
+ if (schema.$ref) {
+ return {refType(schema.$ref, refs)};
+ }
+ if (schema.type === 'array') {
+ const name = schema.items?.$ref
+ ? refType(schema.items.$ref, refs)
+ : schema.items?.type ?? '';
+ return {name}[];
+ }
+ if (schema.anyOf && schema.anyOf.length > 0) {
+ const parts = schema.anyOf.map(tpe => (
+ {schemaType(tpe, refs)}
+ ));
+ return joinReactNodes(parts, ' | ');
+ }
+ return schema.type;
+}
+
+function schemaRequired(
+ schema: OAISchema,
+ property: SchemaProperty
+): React.ReactNode {
+ console.log({schema: schema, property: property.name});
+ if (schema.required?.includes(property.name)) {
+ return Yes;
+ }
+ return No;
+}
+
+function schemaDescription(schema: OAISchema): React.ReactNode {
+ const parts: React.ReactNode[] = [];
+ if (schema.description) {
+ parts.push({schema.description});
+ }
+ if (schema.enum && schema.type === 'string') {
+ const values = schema.enum.map(value => "{value}");
+ if (values.length === 1) {
+ parts.push(Value: {values[0]});
+ } else {
+ parts.push(One of: {joinReactNodes(values, ",")});
+ }
+ }
+
+
+ if (
+ typeof schema.minimum === 'number' &&
+ typeof schema.maximum === 'number'
+ ) {
+ parts.push(
+
+ Range:{' '}
+
+ {schema.minimum} <= x <= {schema.maximum}
+
+
+ );
+ } else if (typeof schema.minimum === 'number') {
+ parts.push(
+
+ Minimum: {schema.minimum}
+
+ );
+ } else if (typeof schema.maximum === 'number') {
+ parts.push(
+
+ Maximum: {schema.maximum}
+
+ );
+ }
+
+ return joinReactNodes(parts, <>>);
+}
+
+function joinReactNodes(values: React.ReactNode[], separator: React.ReactNode) {
+ if (values.length === 0) {
+ return null;
+ }
+ return values.reduce((prev, curr, i) =>
+ i === 0 ? (
+ curr
+ ) : (
+ <>
+ {prev}
+ {separator}
+ {curr}
+ >
+ )
+ );
+}
diff --git a/src/components/openapi/ApiSpec.ts b/src/components/openapi/ApiSpec.ts
new file mode 100644
index 000000000..58bf476e2
--- /dev/null
+++ b/src/components/openapi/ApiSpec.ts
@@ -0,0 +1,87 @@
+import {OAISchema, OAISpec, Operation, SchemaProperty} from './types';
+
+export function newApiSpec(spec: OAISpec): ApiSpec {
+ const components = new Map();
+ const operations: Operation[] = [];
+ for (const path of Object.keys(spec.paths)) {
+ const pathItem = spec.paths[path as keyof typeof spec.paths];
+ for (const method of Object.keys(pathItem)) {
+ const operation = pathItem[method as keyof typeof pathItem];
+ if (operation && 'operationId' in operation) {
+ const requestExample =
+ operation.requestBody?.content?.['application/json']
+ ?.example;
+ const responseExample =
+ operation.responses['200']?.content?.['application/json']
+ ?.example;
+ operations.push({
+ id: `${method.toUpperCase()} ${path}`,
+ method,
+ path,
+ description: operation.description,
+ example:
+ requestExample || responseExample
+ ? {
+ request: requestExample,
+ response: responseExample
+ }
+ : undefined,
+ schema: {
+ requestBody:
+ operation.requestBody?.content?.['application/json']
+ ?.schema,
+ response:
+ operation.responses['200']?.content?.[
+ 'application/json'
+ ]?.schema,
+ parameters: operation.parameters ?? []
+ }
+ });
+ }
+ }
+ }
+ for (const component of Object.keys(spec.components?.schemas ?? {})) {
+ const schema = spec.components?.schemas?.[component];
+ if (!schema) {
+ continue;
+ }
+ components.set(`#/components/schemas/${component}`, schema);
+ }
+ return new ApiSpec(components, operations);
+}
+
+export class ApiSpec {
+ constructor(
+ public readonly components: Map,
+ public readonly operations: Operation[]
+ ) {}
+ public findOperation(operation: string): Operation | undefined {
+ for (const op of this.operations) {
+ if (op.id === operation) {
+ return op;
+ }
+ }
+ return undefined;
+ }
+ public ref(ref: string): OAISchema {
+ const component = this.components.get(ref);
+ if (!component) {
+ throw new Error(`$ref not found: ${ref}`);
+ }
+ return component;
+ }
+ public canonical(schema: OAISchema): OAISchema {
+ if (schema.$ref) {
+ return this.canonical(this.ref(schema.$ref));
+ }
+ return schema;
+ }
+ public properties(schema: OAISchema): SchemaProperty[] {
+ if (schema.$ref) {
+ return this.properties(this.ref(schema.$ref));
+ }
+ return Object.entries(schema.properties ?? {}).map(
+ ([name, schema]) => ({name, schema})
+ );
+ }
+}
diff --git a/src/components/openapi/openapi.Sourcegraph.Latest.json b/src/components/openapi/openapi.Sourcegraph.Latest.json
new file mode 100644
index 000000000..4e67dbe10
--- /dev/null
+++ b/src/components/openapi/openapi.Sourcegraph.Latest.json
@@ -0,0 +1,790 @@
+{
+ "openapi": "3.0.0",
+ "info": {
+ "title": "Sourcegraph",
+ "version": "Latest"
+ },
+ "tags": [],
+ "paths": {
+ "/.api/cody/context": {
+ "post": {
+ "operationId": "CodyService_context",
+ "description": "Send a natural language query with a list of repositories, and Cody locates related code examples from those repos.",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "The request has succeeded.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CodyContextResponse"
+ },
+ "example": {
+ "results": [
+ {
+ "blob": {
+ "path": "vscode/src/chat/chat-view/ChatController.ts",
+ "repository": {
+ "id": "UmVwb3NpdG9yeToyNzU5OQ==",
+ "name": "github.com/sourcegraph/cody"
+ },
+ "commit": {
+ "oid": "fdcc8a185b21c81d1987bc1daf2c29cec3d19b06"
+ },
+ "url": "/github.com/sourcegraph/cody@fdcc8a185b21c81d1987bc1daf2c29cec3d19b06/-/blob/vscode/src/chat/chat-view/ChatController.ts"
+ },
+ "startLine": 156,
+ "endLine": 181,
+ "chunkContent": "\n/**\n * ChatController is the view controller class for the chat panel.\n * It handles all events sent from the view, keeps track of the underlying chat model,\n * and interacts with the rest of the extension.\n *\n * Its methods are grouped into the following sections, each of which is demarcated\n * by a comment block (search for \"// #region \"):\n *\n * 1. top-level view action handlers\n * 2. view updaters\n * 3. chat request lifecycle methods\n * 4. session management\n * 5. webview container management\n * 6. other public accessors and mutators\n *\n * The following invariants should be maintained:\n * 1. top-level view action handlers\n * a. should all follow the handle$ACTION naming convention\n * b. should be private (with the existing exceptions)\n * 2. view updaters\n * a. should all follow the post$ACTION naming convention\n * b. should NOT mutate model state\n * 3. Keep the public interface of this class small in order to\n * avoid tight coupling with other classes. If communication\n"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CodyContextRequest"
+ },
+ "example": {
+ "query": "What does ChatController do?",
+ "repos": [
+ {
+ "name": "github.com/sourcegraph/cody"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "/.api/llm/chat/completions": {
+ "post": {
+ "operationId": "LLMService_chatCompletions",
+ "description": "Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation.",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "The request has succeeded.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateChatCompletionResponse"
+ },
+ "example": {
+ "id": "chat-UUID",
+ "created": 1727692163829,
+ "model": "anthropic::2023-06-01::claude-3.5-sonnet",
+ "object": "object",
+ "choices": [
+ {
+ "index": 0,
+ "finish_reason": "stop",
+ "message": {
+ "role": "assistant",
+ "content": "URIs identify, URLs locate"
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateChatCompletionRequest"
+ },
+ "example": {
+ "model": "anthropic::2023-06-01::claude-3.5-sonnet",
+ "max_tokens": 2000,
+ "messages": [
+ {
+ "role": "user",
+ "content": "what is the difference between URI and URL?"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "/.api/llm/models": {
+ "get": {
+ "operationId": "LLMService_list",
+ "description": "Lists the currently available models, and provides basic information about each one such as the owner and availability.",
+ "parameters": [],
+ "responses": {
+ "200": {
+ "description": "The request has succeeded.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAIListModelsResponse"
+ },
+ "example": {
+ "object": "list",
+ "data": [
+ {
+ "id": "anthropic::2023-06-01::claude-3.5-sonnet",
+ "object": "model",
+ "created": 0,
+ "owned_by": "anthropic"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/.api/llm/models/{modelId}": {
+ "get": {
+ "operationId": "LLMService_retrieveModel",
+ "description": "Retrieves a model instance, providing basic information about the model such as the owner and permissioning.",
+ "parameters": [
+ {
+ "name": "modelId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The request has succeeded.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/OAIModel"
+ },
+ "example": {
+ "id": "anthropic::2023-06-01::claude-3.5-sonnet",
+ "object": "model",
+ "created": 0,
+ "owned_by": "anthropic"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "security": [
+ {
+ "SourcegraphTokenAuth": []
+ }
+ ],
+ "components": {
+ "schemas": {
+ "BlobInfo": {
+ "type": "object",
+ "required": [
+ "path",
+ "repository",
+ "commit",
+ "url"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "repository": {
+ "$ref": "#/components/schemas/RepositoryInfo"
+ },
+ "commit": {
+ "$ref": "#/components/schemas/CommitInfo"
+ },
+ "url": {
+ "type": "string"
+ }
+ }
+ },
+ "ChatCompletionChoice": {
+ "type": "object",
+ "required": [
+ "index",
+ "message"
+ ],
+ "properties": {
+ "finish_reason": {
+ "type": "string"
+ },
+ "index": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "$ref": "#/components/schemas/ChatCompletionResponseMessage"
+ },
+ "logprobs": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/ChatCompletionLogprobs"
+ }
+ ],
+ "nullable": true
+ }
+ }
+ },
+ "ChatCompletionLogprobs": {
+ "type": "object",
+ "properties": {
+ "content": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ChatCompletionTokenLogprob"
+ }
+ }
+ }
+ },
+ "ChatCompletionRequestMessage": {
+ "type": "object",
+ "required": [
+ "role",
+ "content"
+ ],
+ "properties": {
+ "role": {
+ "type": "string",
+ "enum": [
+ "user",
+ "assistant",
+ "system"
+ ]
+ },
+ "content": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/MessageContentPart"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "ChatCompletionResponseMessage": {
+ "type": "object",
+ "required": [
+ "role",
+ "content"
+ ],
+ "properties": {
+ "role": {
+ "type": "string",
+ "enum": [
+ "user",
+ "assistant"
+ ]
+ },
+ "content": {
+ "type": "string"
+ }
+ }
+ },
+ "ChatCompletionStreamOptions": {
+ "type": "object",
+ "properties": {
+ "include_usage": {
+ "type": "boolean",
+ "nullable": true
+ }
+ }
+ },
+ "ChatCompletionTokenLogprob": {
+ "type": "object",
+ "required": [
+ "token",
+ "logprob",
+ "bytes",
+ "top_logprobs"
+ ],
+ "properties": {
+ "token": {
+ "type": "string"
+ },
+ "logprob": {
+ "type": "number",
+ "format": "double"
+ },
+ "bytes": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ },
+ "top_logprobs": {
+ "type": "object",
+ "required": [
+ "token",
+ "logprob",
+ "bytes"
+ ],
+ "properties": {
+ "token": {
+ "type": "string"
+ },
+ "logprob": {
+ "type": "number",
+ "format": "double"
+ },
+ "bytes": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "format": "int32"
+ }
+ }
+ },
+ "description": "The template for omitting properties."
+ }
+ }
+ },
+ "CodyContextRequest": {
+ "type": "object",
+ "required": [
+ "query"
+ ],
+ "properties": {
+ "repos": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/RepoSpec"
+ },
+ "description": "The list of repos to search through."
+ },
+ "query": {
+ "type": "string",
+ "description": "The natural language query to find relevant context from the provided list of repos."
+ },
+ "codeResultsCount": {
+ "type": "integer",
+ "format": "int32",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The number of results to return from source code (example: Python or TypeScript).",
+ "default": 15
+ },
+ "textResultsCount": {
+ "type": "integer",
+ "format": "int32",
+ "minimum": 0,
+ "maximum": 100,
+ "description": "The number of results to return from text sources like Markdown.",
+ "default": 5
+ },
+ "filePatterns": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "An optional list of file patterns used to filter the results. The\npatterns are regex strings. For a file chunk to be returned a context\nresult, the path must match at least one of these patterns."
+ },
+ "version": {
+ "type": "string",
+ "enum": [
+ "1.0",
+ "2.0"
+ ],
+ "description": "The version number of the context API\n\nValid versions:\n- \"1.0\": The old context API (default).\n- \"2.0\": The new context API.",
+ "default": "1.0"
+ }
+ }
+ },
+ "CodyContextResponse": {
+ "type": "object",
+ "required": [
+ "results"
+ ],
+ "properties": {
+ "results": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/FileChunkContext"
+ }
+ }
+ }
+ },
+ "CommitInfo": {
+ "type": "object",
+ "required": [
+ "oid"
+ ],
+ "properties": {
+ "oid": {
+ "type": "string"
+ }
+ }
+ },
+ "CompletionUsage": {
+ "type": "object",
+ "required": [
+ "completion_tokens",
+ "prompt_tokens",
+ "total_tokens"
+ ],
+ "properties": {
+ "completion_tokens": {
+ "type": "integer",
+ "format": "int32",
+ "description": "Number of tokens in the generated completion."
+ },
+ "prompt_tokens": {
+ "type": "integer",
+ "format": "int32",
+ "description": "Number of tokens in the prompt."
+ },
+ "total_tokens": {
+ "type": "integer",
+ "format": "int32",
+ "description": "Total number of tokens used in the request (prompt + completion)."
+ }
+ },
+ "description": "Usage statistics for the completion request."
+ },
+ "CreateChatCompletionRequest": {
+ "type": "object",
+ "required": [
+ "model"
+ ],
+ "properties": {
+ "messages": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ChatCompletionRequestMessage"
+ },
+ "description": "A list of messages to start the thread with."
+ },
+ "model": {
+ "type": "string",
+ "description": "A model name using the syntax `${ProviderID}::${APIVersionID}::${ModelID}`:\n- ProviderID: lowercase name of the LLM provider. Example: `\"anthropic\"` in\n`\"anthropic::2023-06-01::claude-3.5-sonnet\"`.\n- APIVersionID: the upstream LLM provider API version. Typically formatted as\na date. Example, `\"2024-02-01\"` in `\"openai::2024-02-01::gpt-4o\"`.\n- ModelID: the name of the model. Example, `\"mixtral-8x7b-instruct\"` in\n`\"mistral::v1::mixtral-8x7b-instruct\"`.\n\nUse `GET /.api/llm/models` to list available models."
+ },
+ "max_tokens": {
+ "type": "integer",
+ "format": "int32",
+ "nullable": true,
+ "maximum": 8000,
+ "description": "The maximum number of tokens that can be generated in the completion."
+ },
+ "logit_bias": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "nullable": true
+ },
+ "logprobs": {
+ "type": "boolean",
+ "nullable": true
+ },
+ "top_logprobs": {
+ "type": "integer",
+ "format": "int32",
+ "nullable": true
+ },
+ "n": {
+ "type": "integer",
+ "format": "int32",
+ "nullable": true
+ },
+ "frequency_penalty": {
+ "type": "number",
+ "format": "double",
+ "nullable": true
+ },
+ "presence_penalty": {
+ "type": "number",
+ "format": "double",
+ "nullable": true
+ },
+ "response_format": {
+ "type": "string",
+ "enum": [
+ "text",
+ "json_object"
+ ],
+ "nullable": true
+ },
+ "seed": {
+ "type": "integer",
+ "format": "int64",
+ "nullable": true
+ },
+ "service_tier": {
+ "type": "string",
+ "nullable": true
+ },
+ "stop": {
+ "anyOf": [
+ {
+ "type": "string"
+ },
+ {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ],
+ "nullable": true
+ },
+ "stream": {
+ "type": "boolean",
+ "nullable": true
+ },
+ "stream_options": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/ChatCompletionStreamOptions"
+ }
+ ],
+ "nullable": true
+ },
+ "temperature": {
+ "type": "number",
+ "format": "float",
+ "nullable": true
+ },
+ "top_p": {
+ "type": "number",
+ "format": "float",
+ "nullable": true
+ },
+ "user": {
+ "type": "string",
+ "nullable": true
+ }
+ }
+ },
+ "CreateChatCompletionResponse": {
+ "type": "object",
+ "required": [
+ "id",
+ "choices",
+ "created",
+ "model",
+ "object"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "choices": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ChatCompletionChoice"
+ }
+ },
+ "created": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "model": {
+ "type": "string"
+ },
+ "service_tier": {
+ "type": "string",
+ "nullable": true
+ },
+ "system_fingerprint": {
+ "type": "string",
+ "nullable": true
+ },
+ "object": {
+ "type": "string",
+ "enum": [
+ "object"
+ ]
+ },
+ "usage": {
+ "type": "object",
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/CompletionUsage"
+ }
+ ],
+ "nullable": true
+ }
+ }
+ },
+ "Error": {
+ "type": "object",
+ "required": [
+ "type",
+ "message"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ },
+ "FileChunkContext": {
+ "type": "object",
+ "required": [
+ "blob",
+ "startLine",
+ "endLine",
+ "chunkContent"
+ ],
+ "properties": {
+ "blob": {
+ "$ref": "#/components/schemas/BlobInfo"
+ },
+ "startLine": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "endLine": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "chunkContent": {
+ "type": "string"
+ }
+ }
+ },
+ "MessageContentPart": {
+ "type": "object",
+ "required": [
+ "type",
+ "text"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": [
+ "text"
+ ]
+ },
+ "text": {
+ "type": "string"
+ }
+ }
+ },
+ "OAIListModelsResponse": {
+ "type": "object",
+ "required": [
+ "object",
+ "data"
+ ],
+ "properties": {
+ "object": {
+ "type": "string",
+ "enum": [
+ "list"
+ ]
+ },
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/OAIModel"
+ }
+ }
+ }
+ },
+ "OAIModel": {
+ "type": "object",
+ "required": [
+ "id",
+ "object",
+ "created",
+ "owned_by"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "The model identifier, which can be referenced in the API endpoints."
+ },
+ "object": {
+ "type": "string",
+ "enum": [
+ "model"
+ ],
+ "description": "The object type, which is always \"model\"."
+ },
+ "created": {
+ "type": "integer",
+ "format": "int64",
+ "description": "The Unix timestamp (in seconds) when the model was created."
+ },
+ "owned_by": {
+ "type": "string",
+ "description": "The organization that owns the model."
+ }
+ },
+ "description": "Describes an OpenAI model offering that can be used with the API."
+ },
+ "RepoSpec": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the repository."
+ },
+ "id": {
+ "type": "string",
+ "description": "The ID of the repository."
+ }
+ },
+ "description": "RepoSpec matches a repository either by name or ID.\n\nExactly one of the properties must be defined. For example, the message\n`{id:\"id\", name:\"name\"}` is invalid because it declares both id and name."
+ },
+ "RepositoryInfo": {
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ },
+ "Versions": {
+ "type": "string",
+ "enum": [
+ "V5_7",
+ "V5_8",
+ "Latest"
+ ]
+ }
+ },
+ "securitySchemes": {
+ "SourcegraphTokenAuth": {
+ "type": "apiKey",
+ "in": "header",
+ "name": "Authorization",
+ "description": "Authenticate to Sourcegraph APIs with the HTTP header \"Authorization\" using\nthe following formatting:\n\n```\nAuthorization: token TOKEN_VALUE\n```\nIn most cases, a Sourcegraph access token looks like this `sgp_asdadakjaaaaaaabbbbbbssswwwwaaal2131kasdaakkkkkq21asdasaa`.\n\nIn rare cases, you may encounter other kinds of token formats, which are documented in the table below.\n\n| Token Name | Description | Type | Regular Expression | |\n| -------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------- | ------------------------- | ----------------------- |\n| Sourcegraph Access Token (v3) | Token used to access the Sourcegraph GraphQL API | User-generated | `sgp_(?:[a-fA-F0-9]{16}\\|local)_[a-fA-F0-9]{40}` |\n| Sourcegraph Access Token (v2, deprecated) | Token used to access the Sourcegraph GraphQL API | User-generated | `sgp_[a-fA-F0-9]{40}` | |\n| Sourcegraph Access Token (v1, deprecated) | Token used to access the Sourcegraph GraphQL API | User-generated | `[a-fA-F0-9]{40}` | |\n| Sourcegraph Dotcom User Gateway Access Token | Token used to grant sourcegraph.com users access to Cody | Backend (not user-visible) | `sgd_[a-fA-F0-9]{64}` | |\n| Sourcegraph License Key Token | Token used for product subscriptions, derived from a Sourcegraph license key | Backend (not user-visible) | `slk_[a-fA-F0-9]{64}` | |\n| Sourcegraph Enterprise subscription (aka \"product subscription\") Token | Token used for Enterprise subscriptions, derived from a Sourcegraph license key | Backend (not user-visible) | `sgs_[a-fA-F0-9]{64}` | |"
+ }
+ }
+ }
+}
diff --git a/src/components/openapi/types.ts b/src/components/openapi/types.ts
new file mode 100644
index 000000000..eb427ad16
--- /dev/null
+++ b/src/components/openapi/types.ts
@@ -0,0 +1,105 @@
+export interface Operation {
+ id: string
+ path: string
+ description: string
+ method: string
+ example?: {
+ request?: any
+ response: any
+ }
+ schema: {
+ parameters: OAIParameter[]
+ requestBody?: any
+ response?: any
+ }
+}
+export interface OAISpec {
+ openapi: string
+ info: {
+ title: string
+ version: string
+ },
+ tags: {
+ name: string
+ description?: string
+ }[],
+ paths: {
+ [key: string]: OAIPathItem
+ }
+ components?: {
+ schemas?: {
+ [key: string]: OAISchema
+ }
+ }
+ securitySchemes?: {
+ [key: string]: OAISecurityScheme
+ }
+}
+export interface OAISecurityScheme {
+ type: string
+ in: string
+ name: string
+ description: string
+}
+export interface OAIPathItem {
+ [key: string]: OAIOperation
+}
+export interface OAISchema {
+ type?: string
+ $ref?: string
+ nullable?: boolean
+ format?: string
+ minimum?: number
+ maximum?: number
+ default?: any
+ anyOf?: OAISchema[]
+ items?: OAISchema
+ required?: string[]
+ enum?: string[]
+ properties?: {
+ [key: string]: OAISchema
+ }
+ description?: string
+}
+export interface OAIParameter {
+ name: string
+ in: string
+ required: boolean
+ schema: OAISchema
+}
+export interface OAIOperation {
+ operationId: string
+ description: string
+ parameters?: OAIParameter[]
+ responses: {
+ '200': {
+ description: string
+ content: {
+ 'application/json': {
+ schema: OAISchema
+ example?: any
+ examples?: {
+ [key: string]: any
+ }
+ }
+ }
+ }
+ }
+ requestBody?: {
+ required: boolean
+ content: {
+ 'application/json': {
+ schema: OAISchema
+ example?: any
+ examples?: {
+ [key: string]: any
+ }
+ }
+ }
+ }
+}
+
+export interface SchemaProperty {
+ name: string;
+ schema: OAISchema;
+}
diff --git a/src/data/navigation.ts b/src/data/navigation.ts
index 0a70c1909..7b9a9cb5b 100644
--- a/src/data/navigation.ts
+++ b/src/data/navigation.ts
@@ -294,6 +294,10 @@ export const navigation: NavigationItem[] = [
title: "Sourcegraph GraphQL API",
href: "/api/graphql",
},
+ {
+ title: "Sourcegraph Cody API",
+ href: "/api/cody",
+ },
{
title: "Sourcegraph Stream API",
href: "/api/stream_api",